blob: f1b8d2a874f37d9d774f32d7789dcf6cb640c7da [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
29/* Growarray to store the names of sourced scripts.
30 * 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 */
41 int sn_pr_force; /* forceit: profile defined functions */
42 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{
805 tm->tv_usec = 0;
806 tm->tv_sec = 0;
807}
808
809/*
810 * Store the current time in "tm".
811 */
812 void
813profile_start(tm)
814 proftime_T *tm;
815{
816 gettimeofday(tm, NULL);
817}
818
819/*
820 * Compute the elapsed time from "tm" till now and store in "tm".
821 */
822 void
823profile_end(tm)
824 proftime_T *tm;
825{
826 proftime_T now;
827
828 gettimeofday(&now, NULL);
829 tm->tv_usec = now.tv_usec - tm->tv_usec;
830 tm->tv_sec = now.tv_sec - tm->tv_sec;
831 if (tm->tv_usec < 0)
832 {
833 tm->tv_usec += 1000000;
834 --tm->tv_sec;
835 }
836}
837
838/*
839 * Subtract the time "tm2" from "tm".
840 */
841 void
842profile_sub(tm, tm2)
843 proftime_T *tm, *tm2;
844{
845 tm->tv_usec -= tm2->tv_usec;
846 tm->tv_sec -= tm2->tv_sec;
847 if (tm->tv_usec < 0)
848 {
849 tm->tv_usec += 1000000;
850 --tm->tv_sec;
851 }
852}
853
854/*
855 * Add the time "tm2" to "tm".
856 */
857 void
858profile_add(tm, tm2)
859 proftime_T *tm, *tm2;
860{
861 tm->tv_usec += tm2->tv_usec;
862 tm->tv_sec += tm2->tv_sec;
863 if (tm->tv_usec >= 1000000)
864 {
865 tm->tv_usec -= 1000000;
866 ++tm->tv_sec;
867 }
868}
869
870/*
871 * Get the current waittime.
872 */
873 void
874profile_get_wait(tm)
875 proftime_T *tm;
876{
877 *tm = prof_wait_time;
878}
879
880/*
881 * Subtract the passed waittime since "tm" from "tma".
882 */
883 void
884profile_sub_wait(tm, tma)
885 proftime_T *tm, *tma;
886{
887 proftime_T tm3 = prof_wait_time;
888
889 profile_sub(&tm3, tm);
890 profile_sub(tma, &tm3);
891}
892
893/*
894 * Return TRUE if "tm1" and "tm2" are equal.
895 */
896 int
897profile_equal(tm1, tm2)
898 proftime_T *tm1, *tm2;
899{
900 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
901}
902
903/*
904 * Return a string that represents a time.
905 * Uses a static buffer!
906 */
907 char *
908profile_msg(tm)
909 proftime_T *tm;
910{
911 static char buf[50];
912
913 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
914 return buf;
915}
916
917static char_u *profile_fname = NULL;
918
919/*
920 * ":profile cmd args"
921 */
922 void
923ex_profile(eap)
924 exarg_T *eap;
925{
926 char_u *e;
927 int len;
928
929 e = skiptowhite(eap->arg);
930 len = e - eap->arg;
931 e = skipwhite(e);
932
933 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
934 {
935 vim_free(profile_fname);
936 profile_fname = vim_strsave(e);
937 do_profiling = TRUE;
938 profile_zero(&prof_wait_time);
939 set_vim_var_nr(VV_PROFILING, 1L);
940 }
941 else if (!do_profiling)
942 EMSG(_("E750: First use :profile start <fname>"));
943 else
944 {
945 /* The rest is similar to ":breakadd". */
946 ex_breakadd(eap);
947 }
948}
949
950/*
951 * Dump the profiling info.
952 */
953 void
954profile_dump()
955{
956 FILE *fd;
957
958 if (profile_fname != NULL)
959 {
960 fd = fopen((char *)profile_fname, "w");
961 if (fd == NULL)
962 EMSG2(_(e_notopen), profile_fname);
963 else
964 {
965 func_dump_profile(fd);
966 script_dump_profile(fd);
967 fclose(fd);
968 }
969 }
970}
971
972/*
973 * Start profiling script "fp".
974 */
975 static void
976script_do_profile(si)
977 scriptitem_T *si;
978{
979 si->sn_pr_count = 0;
980 profile_zero(&si->sn_pr_total);
981 profile_zero(&si->sn_pr_self);
982
983 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
984 si->sn_prl_idx = -1;
985 si->sn_prof_on = TRUE;
986 si->sn_pr_nest = 0;
987}
988
989/*
990 * save time when starting to invoke another script or function.
991 */
992 void
993script_prof_save(tm)
994 proftime_T *tm; /* place to store wait time */
995{
996 scriptitem_T *si;
997
998 if (current_SID > 0 && current_SID <= script_items.ga_len)
999 {
1000 si = &SCRIPT_ITEM(current_SID);
1001 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
1002 profile_start(&si->sn_pr_child);
1003 }
1004 profile_get_wait(tm);
1005}
1006
1007/*
1008 * Count time spent in children after invoking another script or function.
1009 */
1010 void
1011script_prof_restore(tm)
1012 proftime_T *tm;
1013{
1014 scriptitem_T *si;
1015
1016 if (current_SID > 0 && current_SID <= script_items.ga_len)
1017 {
1018 si = &SCRIPT_ITEM(current_SID);
1019 if (si->sn_prof_on && --si->sn_pr_nest == 0)
1020 {
1021 profile_end(&si->sn_pr_child);
1022 profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */
1023 profile_add(&si->sn_pr_children, &si->sn_pr_child);
1024 profile_add(&si->sn_prl_children, &si->sn_pr_child);
1025 }
1026 }
1027}
1028
1029static proftime_T inchar_time;
1030
1031/*
1032 * Called when starting to wait for the user to type a character.
1033 */
1034 void
1035prof_inchar_enter()
1036{
1037 profile_start(&inchar_time);
1038}
1039
1040/*
1041 * Called when finished waiting for the user to type a character.
1042 */
1043 void
1044prof_inchar_exit()
1045{
1046 profile_end(&inchar_time);
1047 profile_add(&prof_wait_time, &inchar_time);
1048}
1049
1050/*
1051 * Dump the profiling results for all scripts in file "fd".
1052 */
1053 static void
1054script_dump_profile(fd)
1055 FILE *fd;
1056{
1057 int id;
1058 scriptitem_T *si;
1059 int i;
1060 FILE *sfd;
1061 sn_prl_T *pp;
1062
1063 for (id = 1; id <= script_items.ga_len; ++id)
1064 {
1065 si = &SCRIPT_ITEM(id);
1066 if (si->sn_prof_on)
1067 {
1068 fprintf(fd, "SCRIPT %s\n", si->sn_name);
1069 if (si->sn_pr_count == 1)
1070 fprintf(fd, "Sourced 1 time\n");
1071 else
1072 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
1073 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
1074 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
1075 fprintf(fd, "\n");
1076 fprintf(fd, "count total (s) self (s)\n");
1077
1078 sfd = fopen((char *)si->sn_name, "r");
1079 if (sfd == NULL)
1080 fprintf(fd, "Cannot open file!\n");
1081 else
1082 {
1083 for (i = 0; i < si->sn_prl_ga.ga_len; ++i)
1084 {
1085 if (vim_fgets(IObuff, IOSIZE, sfd))
1086 break;
1087 pp = &PRL_ITEM(si, i);
1088 if (pp->snp_count > 0)
1089 {
1090 fprintf(fd, "%5d ", pp->snp_count);
1091 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1092 fprintf(fd, " ");
1093 else
1094 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1095 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1096 }
1097 else
1098 fprintf(fd, " ");
1099 fprintf(fd, "%s", IObuff);
1100 }
1101 fclose(sfd);
1102 }
1103 fprintf(fd, "\n");
1104 }
1105 }
1106}
1107
1108/*
1109 * Return TRUE when a function defined in the current script should be
1110 * profiled.
1111 */
1112 int
1113prof_def_func()
1114{
1115 scriptitem_T *si = &SCRIPT_ITEM(current_SID);
1116
1117 return si->sn_pr_force;
1118}
1119
1120# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121#endif
1122
1123/*
1124 * If 'autowrite' option set, try to write the file.
1125 * Careful: autocommands may make "buf" invalid!
1126 *
1127 * return FAIL for failure, OK otherwise
1128 */
1129 int
1130autowrite(buf, forceit)
1131 buf_T *buf;
1132 int forceit;
1133{
1134 if (!(p_aw || p_awa) || !p_write
1135#ifdef FEAT_QUICKFIX
1136 /* never autowrite a "nofile" or "nowrite" buffer */
1137 || bt_dontwrite(buf)
1138#endif
1139 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
1140 return FAIL;
1141 return buf_write_all(buf, forceit);
1142}
1143
1144/*
1145 * flush all buffers, except the ones that are readonly
1146 */
1147 void
1148autowrite_all()
1149{
1150 buf_T *buf;
1151
1152 if (!(p_aw || p_awa) || !p_write)
1153 return;
1154 for (buf = firstbuf; buf; buf = buf->b_next)
1155 if (bufIsChanged(buf) && !buf->b_p_ro)
1156 {
1157 (void)buf_write_all(buf, FALSE);
1158#ifdef FEAT_AUTOCMD
1159 /* an autocommand may have deleted the buffer */
1160 if (!buf_valid(buf))
1161 buf = firstbuf;
1162#endif
1163 }
1164}
1165
1166/*
1167 * return TRUE if buffer was changed and cannot be abandoned.
1168 */
1169/*ARGSUSED*/
1170 int
1171check_changed(buf, checkaw, mult_win, forceit, allbuf)
1172 buf_T *buf;
1173 int checkaw; /* do autowrite if buffer was changed */
1174 int mult_win; /* check also when several wins for the buf */
1175 int forceit;
1176 int allbuf; /* may write all buffers */
1177{
1178 if ( !forceit
1179 && bufIsChanged(buf)
1180 && (mult_win || buf->b_nwindows <= 1)
1181 && (!checkaw || autowrite(buf, forceit) == FAIL))
1182 {
1183#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1184 if ((p_confirm || cmdmod.confirm) && p_write)
1185 {
1186 buf_T *buf2;
1187 int count = 0;
1188
1189 if (allbuf)
1190 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1191 if (bufIsChanged(buf2)
1192 && (buf2->b_ffname != NULL
1193# ifdef FEAT_BROWSE
1194 || cmdmod.browse
1195# endif
1196 ))
1197 ++count;
1198# ifdef FEAT_AUTOCMD
1199 if (!buf_valid(buf))
1200 /* Autocommand deleted buffer, oops! It's not changed now. */
1201 return FALSE;
1202# endif
1203 dialog_changed(buf, count > 1);
1204# ifdef FEAT_AUTOCMD
1205 if (!buf_valid(buf))
1206 /* Autocommand deleted buffer, oops! It's not changed now. */
1207 return FALSE;
1208# endif
1209 return bufIsChanged(buf);
1210 }
1211#endif
1212 EMSG(_(e_nowrtmsg));
1213 return TRUE;
1214 }
1215 return FALSE;
1216}
1217
1218#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
1219
1220#if defined(FEAT_BROWSE) || defined(PROTO)
1221/*
1222 * When wanting to write a file without a file name, ask the user for a name.
1223 */
1224 void
1225browse_save_fname(buf)
1226 buf_T *buf;
1227{
1228 if (buf->b_fname == NULL)
1229 {
1230 char_u *fname;
1231
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001232 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
1233 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 if (fname != NULL)
1235 {
1236 if (setfname(buf, fname, NULL, TRUE) == OK)
1237 buf->b_flags |= BF_NOTEDITED;
1238 vim_free(fname);
1239 }
1240 }
1241}
1242#endif
1243
1244/*
1245 * Ask the user what to do when abondoning a changed buffer.
1246 * Must check 'write' option first!
1247 */
1248 void
1249dialog_changed(buf, checkall)
1250 buf_T *buf;
1251 int checkall; /* may abandon all changed buffers */
1252{
1253 char_u buff[IOSIZE];
1254 int ret;
1255 buf_T *buf2;
1256
1257 dialog_msg(buff, _("Save changes to \"%.*s\"?"),
1258 (buf->b_fname != NULL) ?
1259 buf->b_fname : (char_u *)_("Untitled"));
1260 if (checkall)
1261 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
1262 else
1263 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
1264
1265 if (ret == VIM_YES)
1266 {
1267#ifdef FEAT_BROWSE
1268 /* May get file name, when there is none */
1269 browse_save_fname(buf);
1270#endif
1271 if (buf->b_fname != NULL) /* didn't hit Cancel */
1272 (void)buf_write_all(buf, FALSE);
1273 }
1274 else if (ret == VIM_NO)
1275 {
1276 unchanged(buf, TRUE);
1277 }
1278 else if (ret == VIM_ALL)
1279 {
1280 /*
1281 * Write all modified files that can be written.
1282 * Skip readonly buffers, these need to be confirmed
1283 * individually.
1284 */
1285 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1286 {
1287 if (bufIsChanged(buf2)
1288 && (buf2->b_ffname != NULL
1289#ifdef FEAT_BROWSE
1290 || cmdmod.browse
1291#endif
1292 )
1293 && !buf2->b_p_ro)
1294 {
1295#ifdef FEAT_BROWSE
1296 /* May get file name, when there is none */
1297 browse_save_fname(buf2);
1298#endif
1299 if (buf2->b_fname != NULL) /* didn't hit Cancel */
1300 (void)buf_write_all(buf2, FALSE);
1301#ifdef FEAT_AUTOCMD
1302 /* an autocommand may have deleted the buffer */
1303 if (!buf_valid(buf2))
1304 buf2 = firstbuf;
1305#endif
1306 }
1307 }
1308 }
1309 else if (ret == VIM_DISCARDALL)
1310 {
1311 /*
1312 * mark all buffers as unchanged
1313 */
1314 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1315 unchanged(buf2, TRUE);
1316 }
1317}
1318#endif
1319
1320/*
1321 * Return TRUE if the buffer "buf" can be abandoned, either by making it
1322 * hidden, autowriting it or unloading it.
1323 */
1324 int
1325can_abandon(buf, forceit)
1326 buf_T *buf;
1327 int forceit;
1328{
1329 return ( P_HID(buf)
1330 || !bufIsChanged(buf)
1331 || buf->b_nwindows > 1
1332 || autowrite(buf, forceit) == OK
1333 || forceit);
1334}
1335
1336/*
1337 * Return TRUE if any buffer was changed and cannot be abandoned.
1338 * That changed buffer becomes the current buffer.
1339 */
1340 int
1341check_changed_any(hidden)
1342 int hidden; /* Only check hidden buffers */
1343{
1344 buf_T *buf;
1345 int save;
1346#ifdef FEAT_WINDOWS
1347 win_T *wp;
1348#endif
1349
1350 for (;;)
1351 {
1352 /* check curbuf first: if it was changed we can't abandon it */
1353 if (!hidden && curbufIsChanged())
1354 buf = curbuf;
1355 else
1356 {
1357 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1358 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
1359 break;
1360 }
1361 if (buf == NULL) /* No buffers changed */
1362 return FALSE;
1363
1364 if (check_changed(buf, p_awa, TRUE, FALSE, TRUE) && buf_valid(buf))
1365 break; /* didn't save - still changes */
1366 }
1367
1368 exiting = FALSE;
1369#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1370 /*
1371 * When ":confirm" used, don't give an error message.
1372 */
1373 if (!(p_confirm || cmdmod.confirm))
1374#endif
1375 {
1376 /* There must be a wait_return for this message, do_buffer()
1377 * may cause a redraw. But wait_return() is a no-op when vgetc()
1378 * is busy (Quit used from window menu), then make sure we don't
1379 * cause a scroll up. */
1380 if (vgetc_busy)
1381 {
1382 msg_row = cmdline_row;
1383 msg_col = 0;
1384 msg_didout = FALSE;
1385 }
1386 if (EMSG2(_("E162: No write since last change for buffer \"%s\""),
1387 buf_spname(buf) != NULL ? (char_u *)buf_spname(buf) :
1388 buf->b_fname))
1389 {
1390 save = no_wait_return;
1391 no_wait_return = FALSE;
1392 wait_return(FALSE);
1393 no_wait_return = save;
1394 }
1395 }
1396
1397#ifdef FEAT_WINDOWS
1398 /* Try to find a window that contains the buffer. */
1399 if (buf != curbuf)
1400 for (wp = firstwin; wp != NULL; wp = wp->w_next)
1401 if (wp->w_buffer == buf)
1402 {
1403 win_goto(wp);
1404# ifdef FEAT_AUTOCMD
1405 /* Paranoia: did autocms wipe out the buffer with changes? */
1406 if (!buf_valid(buf))
1407 return TRUE;
1408# endif
1409 break;
1410 }
1411#endif
1412
1413 /* Open the changed buffer in the current window. */
1414 if (buf != curbuf)
1415 set_curbuf(buf, DOBUF_GOTO);
1416
1417 return TRUE;
1418}
1419
1420/*
1421 * return FAIL if there is no file name, OK if there is one
1422 * give error message for FAIL
1423 */
1424 int
1425check_fname()
1426{
1427 if (curbuf->b_ffname == NULL)
1428 {
1429 EMSG(_(e_noname));
1430 return FAIL;
1431 }
1432 return OK;
1433}
1434
1435/*
1436 * flush the contents of a buffer, unless it has no file name
1437 *
1438 * return FAIL for failure, OK otherwise
1439 */
1440 int
1441buf_write_all(buf, forceit)
1442 buf_T *buf;
1443 int forceit;
1444{
1445 int retval;
1446#ifdef FEAT_AUTOCMD
1447 buf_T *old_curbuf = curbuf;
1448#endif
1449
1450 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
1451 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
1452 FALSE, forceit, TRUE, FALSE));
1453#ifdef FEAT_AUTOCMD
1454 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001455 {
1456 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001458 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459#endif
1460 return retval;
1461}
1462
1463/*
1464 * Code to handle the argument list.
1465 */
1466
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001467static char_u *do_one_arg __ARGS((char_u *str));
1468static int do_arglist __ARGS((char_u *str, int what, int after));
1469static void alist_check_arg_idx __ARGS((void));
1470static int editing_arg_idx __ARGS((win_T *win));
1471#ifdef FEAT_LISTCMDS
1472static int alist_add_list __ARGS((int count, char_u **files, int after));
1473#endif
1474#define AL_SET 1
1475#define AL_ADD 2
1476#define AL_DEL 3
1477
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001479 * Isolate one argument, taking backticks.
1480 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 * Return a pointer to the start of the next argument.
1482 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001483 static char_u *
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484do_one_arg(str)
1485 char_u *str;
1486{
1487 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 int inbacktick;
1489
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 inbacktick = FALSE;
1491 for (p = str; *str; ++str)
1492 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001493 /* When the backslash is used for escaping the special meaning of a
1494 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 if (rem_backslash(str))
1496 {
1497 *p++ = *str++;
1498 *p++ = *str;
1499 }
1500 else
1501 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001502 /* An item ends at a space not in backticks */
1503 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001505 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001507 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 }
1509 }
1510 str = skipwhite(str);
1511 *p = NUL;
1512
1513 return str;
1514}
1515
Bram Moolenaar86b68352004-12-27 21:59:20 +00001516/*
1517 * Separate the arguments in "str" and return a list of pointers in the
1518 * growarray "gap".
1519 */
1520 int
1521get_arglist(gap, str)
1522 garray_T *gap;
1523 char_u *str;
1524{
1525 ga_init2(gap, (int)sizeof(char_u *), 20);
1526 while (*str != NUL)
1527 {
1528 if (ga_grow(gap, 1) == FAIL)
1529 {
1530 ga_clear(gap);
1531 return FAIL;
1532 }
1533 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
1534
1535 /* Isolate one argument, change it in-place, put a NUL after it. */
1536 str = do_one_arg(str);
1537 }
1538 return OK;
1539}
1540
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541#if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
1542/*
1543 * Redefine the argument list.
1544 */
1545 void
1546set_arglist(str)
1547 char_u *str;
1548{
1549 do_arglist(str, AL_SET, 0);
1550}
1551#endif
1552
1553/*
1554 * "what" == AL_SET: Redefine the argument list to 'str'.
1555 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
1556 * "what" == AL_DEL: remove files in 'str' from the argument list.
1557 *
1558 * Return FAIL for failure, OK otherwise.
1559 */
1560/*ARGSUSED*/
1561 static int
1562do_arglist(str, what, after)
1563 char_u *str;
1564 int what;
1565 int after; /* 0 means before first one */
1566{
1567 garray_T new_ga;
1568 int exp_count;
1569 char_u **exp_files;
1570 int i;
1571#ifdef FEAT_LISTCMDS
1572 char_u *p;
1573 int match;
1574#endif
1575
1576 /*
1577 * Collect all file name arguments in "new_ga".
1578 */
Bram Moolenaar86b68352004-12-27 21:59:20 +00001579 if (get_arglist(&new_ga, str) == FAIL)
1580 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001581
1582#ifdef FEAT_LISTCMDS
1583 if (what == AL_DEL)
1584 {
1585 regmatch_T regmatch;
1586 int didone;
1587
1588 /*
1589 * Delete the items: use each item as a regexp and find a match in the
1590 * argument list.
1591 */
1592#ifdef CASE_INSENSITIVE_FILENAME
1593 regmatch.rm_ic = TRUE; /* Always ignore case */
1594#else
1595 regmatch.rm_ic = FALSE; /* Never ignore case */
1596#endif
1597 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
1598 {
1599 p = ((char_u **)new_ga.ga_data)[i];
1600 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
1601 if (p == NULL)
1602 break;
1603 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1604 if (regmatch.regprog == NULL)
1605 {
1606 vim_free(p);
1607 break;
1608 }
1609
1610 didone = FALSE;
1611 for (match = 0; match < ARGCOUNT; ++match)
1612 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1613 (colnr_T)0))
1614 {
1615 didone = TRUE;
1616 vim_free(ARGLIST[match].ae_fname);
1617 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
1618 (ARGCOUNT - match - 1) * sizeof(aentry_T));
1619 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 if (curwin->w_arg_idx > match)
1621 --curwin->w_arg_idx;
1622 --match;
1623 }
1624
1625 vim_free(regmatch.regprog);
1626 vim_free(p);
1627 if (!didone)
1628 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
1629 }
1630 ga_clear(&new_ga);
1631 }
1632 else
1633#endif
1634 {
1635 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1636 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1637 ga_clear(&new_ga);
1638 if (i == FAIL)
1639 return FAIL;
1640 if (exp_count == 0)
1641 {
1642 EMSG(_(e_nomatch));
1643 return FAIL;
1644 }
1645
1646#ifdef FEAT_LISTCMDS
1647 if (what == AL_ADD)
1648 {
1649 (void)alist_add_list(exp_count, exp_files, after);
1650 vim_free(exp_files);
1651 }
1652 else /* what == AL_SET */
1653#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00001654 alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001655 }
1656
1657 alist_check_arg_idx();
1658
1659 return OK;
1660}
1661
1662/*
1663 * Check the validity of the arg_idx for each other window.
1664 */
1665 static void
1666alist_check_arg_idx()
1667{
1668#ifdef FEAT_WINDOWS
1669 win_T *win;
1670
1671 for (win = firstwin; win != NULL; win = win->w_next)
1672 if (win->w_alist == curwin->w_alist)
1673 check_arg_idx(win);
1674#else
1675 check_arg_idx(curwin);
1676#endif
1677}
1678
1679/*
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001680 * Return TRUE if window "win" is editing then file at the current argument
1681 * index.
1682 */
1683 static int
1684editing_arg_idx(win)
1685 win_T *win;
1686{
1687 return !(win->w_arg_idx >= WARGCOUNT(win)
1688 || (win->w_buffer->b_fnum
1689 != WARGLIST(win)[win->w_arg_idx].ae_fnum
1690 && (win->w_buffer->b_ffname == NULL
1691 || !(fullpathcmp(
1692 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1693 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
1694}
1695
1696/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 * Check if window "win" is editing the w_arg_idx file in its argument list.
1698 */
1699 void
1700check_arg_idx(win)
1701 win_T *win;
1702{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001703 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704 {
1705 /* We are not editing the current entry in the argument list.
1706 * Set "arg_had_last" if we are editing the last one. */
1707 win->w_arg_idx_invalid = TRUE;
1708 if (win->w_arg_idx != WARGCOUNT(win) - 1
1709 && arg_had_last == FALSE
1710#ifdef FEAT_WINDOWS
1711 && ALIST(win) == &global_alist
1712#endif
1713 && GARGCOUNT > 0
1714 && win->w_arg_idx < GARGCOUNT
1715 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1716 || (win->w_buffer->b_ffname != NULL
1717 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
1718 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
1719 arg_had_last = TRUE;
1720 }
1721 else
1722 {
1723 /* We are editing the current entry in the argument list.
1724 * Set "arg_had_last" if it's also the last one */
1725 win->w_arg_idx_invalid = FALSE;
1726 if (win->w_arg_idx == WARGCOUNT(win) - 1
1727#ifdef FEAT_WINDOWS
1728 && win->w_alist == &global_alist
1729#endif
1730 )
1731 arg_had_last = TRUE;
1732 }
1733}
1734
1735/*
1736 * ":args", ":argslocal" and ":argsglobal".
1737 */
1738 void
1739ex_args(eap)
1740 exarg_T *eap;
1741{
1742 int i;
1743
1744 if (eap->cmdidx != CMD_args)
1745 {
1746#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1747 alist_unlink(ALIST(curwin));
1748 if (eap->cmdidx == CMD_argglobal)
1749 ALIST(curwin) = &global_alist;
1750 else /* eap->cmdidx == CMD_arglocal */
1751 alist_new();
1752#else
1753 ex_ni(eap);
1754 return;
1755#endif
1756 }
1757
1758 if (!ends_excmd(*eap->arg))
1759 {
1760 /*
1761 * ":args file ..": define new argument list, handle like ":next"
1762 * Also for ":argslocal file .." and ":argsglobal file ..".
1763 */
1764 ex_next(eap);
1765 }
1766 else
1767#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1768 if (eap->cmdidx == CMD_args)
1769#endif
1770 {
1771 /*
1772 * ":args": list arguments.
1773 */
1774 if (ARGCOUNT > 0)
1775 {
1776 /* Overwrite the command, for a short list there is no scrolling
1777 * required and no wait_return(). */
1778 gotocmdline(TRUE);
1779 for (i = 0; i < ARGCOUNT; ++i)
1780 {
1781 if (i == curwin->w_arg_idx)
1782 msg_putchar('[');
1783 msg_outtrans(alist_name(&ARGLIST[i]));
1784 if (i == curwin->w_arg_idx)
1785 msg_putchar(']');
1786 msg_putchar(' ');
1787 }
1788 }
1789 }
1790#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1791 else if (eap->cmdidx == CMD_arglocal)
1792 {
1793 garray_T *gap = &curwin->w_alist->al_ga;
1794
1795 /*
1796 * ":argslocal": make a local copy of the global argument list.
1797 */
1798 if (ga_grow(gap, GARGCOUNT) == OK)
1799 for (i = 0; i < GARGCOUNT; ++i)
1800 if (GARGLIST[i].ae_fname != NULL)
1801 {
1802 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1803 vim_strsave(GARGLIST[i].ae_fname);
1804 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1805 GARGLIST[i].ae_fnum;
1806 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807 }
1808 }
1809#endif
1810}
1811
1812/*
1813 * ":previous", ":sprevious", ":Next" and ":sNext".
1814 */
1815 void
1816ex_previous(eap)
1817 exarg_T *eap;
1818{
1819 /* If past the last one already, go to the last one. */
1820 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
1821 do_argfile(eap, ARGCOUNT - 1);
1822 else
1823 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1824}
1825
1826/*
1827 * ":rewind", ":first", ":sfirst" and ":srewind".
1828 */
1829 void
1830ex_rewind(eap)
1831 exarg_T *eap;
1832{
1833 do_argfile(eap, 0);
1834}
1835
1836/*
1837 * ":last" and ":slast".
1838 */
1839 void
1840ex_last(eap)
1841 exarg_T *eap;
1842{
1843 do_argfile(eap, ARGCOUNT - 1);
1844}
1845
1846/*
1847 * ":argument" and ":sargument".
1848 */
1849 void
1850ex_argument(eap)
1851 exarg_T *eap;
1852{
1853 int i;
1854
1855 if (eap->addr_count > 0)
1856 i = eap->line2 - 1;
1857 else
1858 i = curwin->w_arg_idx;
1859 do_argfile(eap, i);
1860}
1861
1862/*
1863 * Edit file "argn" of the argument lists.
1864 */
1865 void
1866do_argfile(eap, argn)
1867 exarg_T *eap;
1868 int argn;
1869{
1870 int other;
1871 char_u *p;
1872
1873 if (argn < 0 || argn >= ARGCOUNT)
1874 {
1875 if (ARGCOUNT <= 1)
1876 EMSG(_("E163: There is only one file to edit"));
1877 else if (argn < 0)
1878 EMSG(_("E164: Cannot go before first file"));
1879 else
1880 EMSG(_("E165: Cannot go beyond last file"));
1881 }
1882 else
1883 {
1884 setpcmark();
1885#ifdef FEAT_GUI
1886 need_mouse_correct = TRUE;
1887#endif
1888
1889#ifdef FEAT_WINDOWS
1890 if (*eap->cmd == 's') /* split window first */
1891 {
1892 if (win_split(0, 0) == FAIL)
1893 return;
1894# ifdef FEAT_SCROLLBIND
1895 curwin->w_p_scb = FALSE;
1896# endif
1897 }
1898 else
1899#endif
1900 {
1901 /*
1902 * if 'hidden' set, only check for changed file when re-editing
1903 * the same buffer
1904 */
1905 other = TRUE;
1906 if (P_HID(curbuf))
1907 {
1908 p = fix_fname(alist_name(&ARGLIST[argn]));
1909 other = otherfile(p);
1910 vim_free(p);
1911 }
1912 if ((!P_HID(curbuf) || !other)
1913 && check_changed(curbuf, TRUE, !other, eap->forceit, FALSE))
1914 return;
1915 }
1916
1917 curwin->w_arg_idx = argn;
1918 if (argn == ARGCOUNT - 1
1919#ifdef FEAT_WINDOWS
1920 && curwin->w_alist == &global_alist
1921#endif
1922 )
1923 arg_had_last = TRUE;
1924
1925 /* Edit the file; always use the last known line number. */
1926 (void)do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
1927 eap, ECMD_LAST,
1928 (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) +
1929 (eap->forceit ? ECMD_FORCEIT : 0));
1930
1931 /* like Vi: set the mark where the cursor is in the file. */
1932 if (eap->cmdidx != CMD_argdo)
1933 setmark('\'');
1934 }
1935}
1936
1937/*
1938 * ":next", and commands that behave like it.
1939 */
1940 void
1941ex_next(eap)
1942 exarg_T *eap;
1943{
1944 int i;
1945
1946 /*
1947 * check for changed buffer now, if this fails the argument list is not
1948 * redefined.
1949 */
1950 if ( P_HID(curbuf)
1951 || eap->cmdidx == CMD_snext
1952 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
1953 {
1954 if (*eap->arg != NUL) /* redefine file list */
1955 {
1956 if (do_arglist(eap->arg, AL_SET, 0) == FAIL)
1957 return;
1958 i = 0;
1959 }
1960 else
1961 i = curwin->w_arg_idx + (int)eap->line2;
1962 do_argfile(eap, i);
1963 }
1964}
1965
1966#ifdef FEAT_LISTCMDS
1967/*
1968 * ":argedit"
1969 */
1970 void
1971ex_argedit(eap)
1972 exarg_T *eap;
1973{
1974 int fnum;
1975 int i;
1976 char_u *s;
1977
1978 /* Add the argument to the buffer list and get the buffer number. */
1979 fnum = buflist_add(eap->arg, BLN_LISTED);
1980
1981 /* Check if this argument is already in the argument list. */
1982 for (i = 0; i < ARGCOUNT; ++i)
1983 if (ARGLIST[i].ae_fnum == fnum)
1984 break;
1985 if (i == ARGCOUNT)
1986 {
1987 /* Can't find it, add it to the argument list. */
1988 s = vim_strsave(eap->arg);
1989 if (s == NULL)
1990 return;
1991 i = alist_add_list(1, &s,
1992 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1993 if (i < 0)
1994 return;
1995 curwin->w_arg_idx = i;
1996 }
1997
1998 alist_check_arg_idx();
1999
2000 /* Edit the argument. */
2001 do_argfile(eap, i);
2002}
2003
2004/*
2005 * ":argadd"
2006 */
2007 void
2008ex_argadd(eap)
2009 exarg_T *eap;
2010{
2011 do_arglist(eap->arg, AL_ADD,
2012 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
2013#ifdef FEAT_TITLE
2014 maketitle();
2015#endif
2016}
2017
2018/*
2019 * ":argdelete"
2020 */
2021 void
2022ex_argdelete(eap)
2023 exarg_T *eap;
2024{
2025 int i;
2026 int n;
2027
2028 if (eap->addr_count > 0)
2029 {
2030 /* ":1,4argdel": Delete all arguments in the range. */
2031 if (eap->line2 > ARGCOUNT)
2032 eap->line2 = ARGCOUNT;
2033 n = eap->line2 - eap->line1 + 1;
2034 if (*eap->arg != NUL || n <= 0)
2035 EMSG(_(e_invarg));
2036 else
2037 {
2038 for (i = eap->line1; i <= eap->line2; ++i)
2039 vim_free(ARGLIST[i - 1].ae_fname);
2040 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
2041 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
2042 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 if (curwin->w_arg_idx >= eap->line2)
2044 curwin->w_arg_idx -= n;
2045 else if (curwin->w_arg_idx > eap->line1)
2046 curwin->w_arg_idx = eap->line1;
2047 }
2048 }
2049 else if (*eap->arg == NUL)
2050 EMSG(_(e_argreq));
2051 else
2052 do_arglist(eap->arg, AL_DEL, 0);
2053#ifdef FEAT_TITLE
2054 maketitle();
2055#endif
2056}
2057
2058/*
2059 * ":argdo", ":windo", ":bufdo"
2060 */
2061 void
2062ex_listdo(eap)
2063 exarg_T *eap;
2064{
2065 int i;
2066#ifdef FEAT_WINDOWS
2067 win_T *win;
2068#endif
2069 buf_T *buf;
2070 int next_fnum = 0;
2071#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
2072 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002074 char_u *p_shm_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075
2076#ifndef FEAT_WINDOWS
2077 if (eap->cmdidx == CMD_windo)
2078 {
2079 ex_ni(eap);
2080 return;
2081 }
2082#endif
2083
2084#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
2085 if (eap->cmdidx != CMD_windo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00002086 /* Don't do syntax HL autocommands. Skipping the syntax file is a
2087 * great speed improvement. */
2088 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089#endif
2090
2091 if (eap->cmdidx == CMD_windo
2092 || P_HID(curbuf)
2093 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
2094 {
2095 /* start at the first argument/window/buffer */
2096 i = 0;
2097#ifdef FEAT_WINDOWS
2098 win = firstwin;
2099#endif
2100 /* set pcmark now */
2101 if (eap->cmdidx == CMD_bufdo)
2102 goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
2103 else
2104 setpcmark();
2105 listcmd_busy = TRUE; /* avoids setting pcmark below */
2106
2107 while (!got_int)
2108 {
2109 if (eap->cmdidx == CMD_argdo)
2110 {
2111 /* go to argument "i" */
2112 if (i == ARGCOUNT)
2113 break;
2114 /* Don't call do_argfile() when already there, it will try
2115 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002116 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002117 {
2118 /* Clear 'shm' to avoid that the file message overwrites
2119 * any output from the command. */
2120 p_shm_save = vim_strsave(p_shm);
2121 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002123 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2124 vim_free(p_shm_save);
2125 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002126 if (curwin->w_arg_idx != i)
2127 break;
2128 ++i;
2129 }
2130#ifdef FEAT_WINDOWS
2131 else if (eap->cmdidx == CMD_windo)
2132 {
2133 /* go to window "win" */
2134 if (!win_valid(win))
2135 break;
2136 win_goto(win);
2137 win = win->w_next;
2138 }
2139#endif
2140 else if (eap->cmdidx == CMD_bufdo)
2141 {
2142 /* Remember the number of the next listed buffer, in case
2143 * ":bwipe" is used or autocommands do something strange. */
2144 next_fnum = -1;
2145 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
2146 if (buf->b_p_bl)
2147 {
2148 next_fnum = buf->b_fnum;
2149 break;
2150 }
2151 }
2152
2153 /* execute the command */
2154 do_cmdline(eap->arg, eap->getline, eap->cookie,
2155 DOCMD_VERBOSE + DOCMD_NOWAIT);
2156
2157 if (eap->cmdidx == CMD_bufdo)
2158 {
2159 /* Done? */
2160 if (next_fnum < 0)
2161 break;
2162 /* Check if the buffer still exists. */
2163 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2164 if (buf->b_fnum == next_fnum)
2165 break;
2166 if (buf == NULL)
2167 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002168
2169 /* Go to the next buffer. Clear 'shm' to avoid that the file
2170 * message overwrites any output from the command. */
2171 p_shm_save = vim_strsave(p_shm);
2172 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002174 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2175 vim_free(p_shm_save);
2176
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177 /* If autocommands took us elsewhere, quit here */
2178 if (curbuf->b_fnum != next_fnum)
2179 break;
2180 }
2181
2182 if (eap->cmdidx == CMD_windo)
2183 {
2184 validate_cursor(); /* cursor may have moved */
2185#ifdef FEAT_SCROLLBIND
2186 /* required when 'scrollbind' has been set */
2187 if (curwin->w_p_scb)
2188 do_check_scrollbind(TRUE);
2189#endif
2190 }
2191 }
2192 listcmd_busy = FALSE;
2193 }
2194
2195#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002196 if (save_ei != NULL)
2197 {
2198 au_event_restore(save_ei);
2199 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
2200 curbuf->b_fname, TRUE, curbuf);
2201 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202#endif
2203}
2204
2205/*
2206 * Add files[count] to the arglist of the current window after arg "after".
2207 * The file names in files[count] must have been allocated and are taken over.
2208 * Files[] itself is not taken over.
2209 * Returns index of first added argument. Returns -1 when failed (out of mem).
2210 */
2211 static int
2212alist_add_list(count, files, after)
2213 int count;
2214 char_u **files;
2215 int after; /* where to add: 0 = before first one */
2216{
2217 int i;
2218
2219 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
2220 {
2221 if (after < 0)
2222 after = 0;
2223 if (after > ARGCOUNT)
2224 after = ARGCOUNT;
2225 if (after < ARGCOUNT)
2226 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
2227 (ARGCOUNT - after) * sizeof(aentry_T));
2228 for (i = 0; i < count; ++i)
2229 {
2230 ARGLIST[after + i].ae_fname = files[i];
2231 ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
2232 }
2233 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 if (curwin->w_arg_idx >= after)
2235 ++curwin->w_arg_idx;
2236 return after;
2237 }
2238
2239 for (i = 0; i < count; ++i)
2240 vim_free(files[i]);
2241 return -1;
2242}
2243
2244#endif /* FEAT_LISTCMDS */
2245
2246#ifdef FEAT_EVAL
2247/*
2248 * ":compiler[!] {name}"
2249 */
2250 void
2251ex_compiler(eap)
2252 exarg_T *eap;
2253{
2254 char_u *buf;
2255 char_u *old_cur_comp = NULL;
2256 char_u *p;
2257
2258 if (*eap->arg == NUL)
2259 {
2260 /* List all compiler scripts. */
2261 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
2262 /* ) keep the indenter happy... */
2263 }
2264 else
2265 {
2266 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
2267 if (buf != NULL)
2268 {
2269 if (eap->forceit)
2270 {
2271 /* ":compiler! {name}" sets global options */
2272 do_cmdline_cmd((char_u *)
2273 "command -nargs=* CompilerSet set <args>");
2274 }
2275 else
2276 {
2277 /* ":compiler! {name}" sets local options.
2278 * To remain backwards compatible "current_compiler" is always
2279 * used. A user's compiler plugin may set it, the distributed
2280 * plugin will then skip the settings. Afterwards set
2281 * "b:current_compiler" and restore "current_compiler". */
2282 old_cur_comp = get_var_value((char_u *)"current_compiler");
2283 if (old_cur_comp != NULL)
2284 old_cur_comp = vim_strsave(old_cur_comp);
2285 do_cmdline_cmd((char_u *)
2286 "command -nargs=* CompilerSet setlocal <args>");
2287 }
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00002288 do_unlet((char_u *)"current_compiler", TRUE);
2289 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290
2291 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
2292 if (cmd_runtime(buf, TRUE) == FAIL)
2293 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
2294 vim_free(buf);
2295
2296 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
2297
2298 /* Set "b:current_compiler" from "current_compiler". */
2299 p = get_var_value((char_u *)"current_compiler");
2300 if (p != NULL)
2301 set_internal_string_var((char_u *)"b:current_compiler", p);
2302
2303 /* Restore "current_compiler" for ":compiler {name}". */
2304 if (!eap->forceit)
2305 {
2306 if (old_cur_comp != NULL)
2307 {
2308 set_internal_string_var((char_u *)"current_compiler",
2309 old_cur_comp);
2310 vim_free(old_cur_comp);
2311 }
2312 else
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00002313 do_unlet((char_u *)"current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 }
2315 }
2316 }
2317}
2318#endif
2319
2320/*
2321 * ":runtime {name}"
2322 */
2323 void
2324ex_runtime(eap)
2325 exarg_T *eap;
2326{
2327 cmd_runtime(eap->arg, eap->forceit);
2328}
2329
2330static void source_callback __ARGS((char_u *fname));
2331
2332 static void
2333source_callback(fname)
2334 char_u *fname;
2335{
2336 (void)do_source(fname, FALSE, FALSE);
2337}
2338
2339/*
2340 * Source the file "name" from all directories in 'runtimepath'.
2341 * "name" can contain wildcards.
2342 * When "all" is TRUE, source all files, otherwise only the first one.
2343 * return FAIL when no file could be sourced, OK otherwise.
2344 */
2345 int
2346cmd_runtime(name, all)
2347 char_u *name;
2348 int all;
2349{
2350 return do_in_runtimepath(name, all, source_callback);
2351}
2352
2353/*
2354 * Find "name" in 'runtimepath'. When found, call the "callback" function for
2355 * it.
2356 * When "all" is TRUE repeat for all matches, otherwise only the first one is
2357 * used.
2358 * Returns OK when at least one match found, FAIL otherwise.
2359 */
2360 int
2361do_in_runtimepath(name, all, callback)
2362 char_u *name;
2363 int all;
2364 void (*callback)__ARGS((char_u *fname));
2365{
2366 char_u *rtp;
2367 char_u *np;
2368 char_u *buf;
2369 char_u *rtp_copy;
2370 char_u *tail;
2371 int num_files;
2372 char_u **files;
2373 int i;
2374 int did_one = FALSE;
2375#ifdef AMIGA
2376 struct Process *proc = (struct Process *)FindTask(0L);
2377 APTR save_winptr = proc->pr_WindowPtr;
2378
2379 /* Avoid a requester here for a volume that doesn't exist. */
2380 proc->pr_WindowPtr = (APTR)-1L;
2381#endif
2382
2383 /* Make a copy of 'runtimepath'. Invoking the callback may change the
2384 * value. */
2385 rtp_copy = vim_strsave(p_rtp);
2386 buf = alloc(MAXPATHL);
2387 if (buf != NULL && rtp_copy != NULL)
2388 {
2389 if (p_verbose > 1)
2390 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
2391 (char *)name, (char *)p_rtp);
2392 /* Loop over all entries in 'runtimepath'. */
2393 rtp = rtp_copy;
2394 while (*rtp != NUL && (all || !did_one))
2395 {
2396 /* Copy the path from 'runtimepath' to buf[]. */
2397 copy_option_part(&rtp, buf, MAXPATHL, ",");
2398 if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL)
2399 {
2400 add_pathsep(buf);
2401 tail = buf + STRLEN(buf);
2402
2403 /* Loop over all patterns in "name" */
2404 np = name;
2405 while (*np != NUL && (all || !did_one))
2406 {
2407 /* Append the pattern from "name" to buf[]. */
2408 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
2409 "\t ");
2410
2411 if (p_verbose > 2)
2412 msg_str((char_u *)_("Searching for \"%s\""), buf);
2413
2414 /* Expand wildcards, invoke the callback for each match. */
2415 if (gen_expand_wildcards(1, &buf, &num_files, &files,
2416 EW_FILE) == OK)
2417 {
2418 for (i = 0; i < num_files; ++i)
2419 {
2420 (*callback)(files[i]);
2421 did_one = TRUE;
2422 if (!all)
2423 break;
2424 }
2425 FreeWild(num_files, files);
2426 }
2427 }
2428 }
2429 }
2430 }
2431 vim_free(buf);
2432 vim_free(rtp_copy);
2433 if (p_verbose > 0 && !did_one)
2434 msg_str((char_u *)_("not found in 'runtimepath': \"%s\""), name);
2435
2436#ifdef AMIGA
2437 proc->pr_WindowPtr = save_winptr;
2438#endif
2439
2440 return did_one ? OK : FAIL;
2441}
2442
2443#if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
2444/*
2445 * ":options"
2446 */
2447/*ARGSUSED*/
2448 void
2449ex_options(eap)
2450 exarg_T *eap;
2451{
2452 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
2453}
2454#endif
2455
2456/*
2457 * ":source {fname}"
2458 */
2459 void
2460ex_source(eap)
2461 exarg_T *eap;
2462{
2463#ifdef FEAT_BROWSE
2464 if (cmdmod.browse)
2465 {
2466 char_u *fname = NULL;
2467
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002468 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469 NULL, NULL, BROWSE_FILTER_MACROS, NULL);
2470 if (fname != NULL)
2471 {
2472 cmd_source(fname, eap);
2473 vim_free(fname);
2474 }
2475 }
2476 else
2477#endif
2478 cmd_source(eap->arg, eap);
2479}
2480
2481 static void
2482cmd_source(fname, eap)
2483 char_u *fname;
2484 exarg_T *eap;
2485{
2486 if (*fname == NUL)
2487 EMSG(_(e_argreq));
2488
2489 /* ":source!" read vi commands */
2490 else if (eap != NULL && eap->forceit)
2491 /* Need to execute the commands directly when:
2492 * - ":g" command busy
2493 * - after ":argdo", ":windo" or ":bufdo"
2494 * - another command follows
2495 * - inside a loop
2496 */
2497 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2498#ifdef FEAT_EVAL
2499 || eap->cstack->cs_idx >= 0
2500#endif
2501 );
2502
2503 /* ":source" read ex commands */
2504 else if (do_source(fname, FALSE, FALSE) == FAIL)
2505 EMSG2(_(e_notopen), fname);
2506}
2507
2508/*
2509 * ":source" and associated commands.
2510 */
2511/*
2512 * Structure used to store info for each sourced file.
2513 * It is shared between do_source() and getsourceline().
2514 * This is required, because it needs to be handed to do_cmdline() and
2515 * sourcing can be done recursively.
2516 */
2517struct source_cookie
2518{
2519 FILE *fp; /* opened file for sourcing */
2520 char_u *nextline; /* if not NULL: line that was read ahead */
2521 int finished; /* ":finish" used */
2522#if defined (USE_CRNL) || defined (USE_CR)
2523 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
2524 int error; /* TRUE if LF found after CR-LF */
2525#endif
2526#ifdef FEAT_EVAL
2527 linenr_T breakpoint; /* next line with breakpoint or zero */
2528 char_u *fname; /* name of sourced file */
2529 int dbg_tick; /* debug_tick when breakpoint was set */
2530 int level; /* top nesting level of sourced file */
2531#endif
2532#ifdef FEAT_MBYTE
2533 vimconv_T conv; /* type of conversion */
2534#endif
2535};
2536
2537#ifdef FEAT_EVAL
2538/*
2539 * Return the address holding the next breakpoint line for a source cookie.
2540 */
2541 linenr_T *
2542source_breakpoint(cookie)
2543 void *cookie;
2544{
2545 return &((struct source_cookie *)cookie)->breakpoint;
2546}
2547
2548/*
2549 * Return the address holding the debug tick for a source cookie.
2550 */
2551 int *
2552source_dbg_tick(cookie)
2553 void *cookie;
2554{
2555 return &((struct source_cookie *)cookie)->dbg_tick;
2556}
2557
2558/*
2559 * Return the nesting level for a source cookie.
2560 */
2561 int
2562source_level(cookie)
2563 void *cookie;
2564{
2565 return ((struct source_cookie *)cookie)->level;
2566}
2567#endif
2568
2569static char_u *get_one_sourceline __ARGS((struct source_cookie *sp));
2570
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571#if defined(WIN32) && defined(FEAT_CSCOPE)
2572static FILE *fopen_noinh_readbin __ARGS((char *filename));
2573
2574/*
2575 * Special function to open a file without handle inheritance.
2576 */
2577 static FILE *
2578fopen_noinh_readbin(filename)
2579 char *filename;
2580{
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00002581 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582
2583 if (fd_tmp == -1)
2584 return NULL;
2585 return fdopen(fd_tmp, READBIN);
2586}
2587#endif
2588
2589
2590/*
2591 * do_source: Read the file "fname" and execute its lines as EX commands.
2592 *
2593 * This function may be called recursively!
2594 *
2595 * return FAIL if file could not be opened, OK otherwise
2596 */
2597 int
2598do_source(fname, check_other, is_vimrc)
2599 char_u *fname;
2600 int check_other; /* check for .vimrc and _vimrc */
2601 int is_vimrc; /* call vimrc_found() when file exists */
2602{
2603 struct source_cookie cookie;
2604 char_u *save_sourcing_name;
2605 linenr_T save_sourcing_lnum;
2606 char_u *p;
2607 char_u *fname_exp;
2608 int retval = FAIL;
2609#ifdef FEAT_EVAL
2610 scid_T save_current_SID;
2611 static scid_T last_current_SID = 0;
2612 void *save_funccalp;
2613 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002614 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002615# ifdef UNIX
2616 struct stat st;
2617 int stat_ok;
2618# endif
2619#endif
2620#ifdef STARTUPTIME
2621 struct timeval tv_rel;
2622 struct timeval tv_start;
2623#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002624#ifdef FEAT_PROFILE
2625 proftime_T wait_start;
2626#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002627
2628#ifdef RISCOS
2629 p = mch_munge_fname(fname);
2630#else
2631 p = expand_env_save(fname);
2632#endif
2633 if (p == NULL)
2634 return retval;
2635 fname_exp = fix_fname(p);
2636 vim_free(p);
2637 if (fname_exp == NULL)
2638 return retval;
2639#ifdef MACOS_CLASSIC
2640 slash_n_colon_adjust(fname_exp);
2641#endif
2642 if (mch_isdir(fname_exp))
2643 {
2644 msg_str((char_u *)_("Cannot source a directory: \"%s\""), fname);
2645 goto theend;
2646 }
2647
2648#if defined(WIN32) && defined(FEAT_CSCOPE)
2649 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2650#else
2651 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2652#endif
2653 if (cookie.fp == NULL && check_other)
2654 {
2655 /*
2656 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
2657 * and ".exrc" by "_exrc" or vice versa.
2658 */
2659 p = gettail(fname_exp);
2660 if ((*p == '.' || *p == '_')
2661 && (STRICMP(p + 1, "vimrc") == 0
2662 || STRICMP(p + 1, "gvimrc") == 0
2663 || STRICMP(p + 1, "exrc") == 0))
2664 {
2665 if (*p == '_')
2666 *p = '.';
2667 else
2668 *p = '_';
2669#if defined(WIN32) && defined(FEAT_CSCOPE)
2670 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2671#else
2672 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2673#endif
2674 }
2675 }
2676
2677 if (cookie.fp == NULL)
2678 {
2679 if (p_verbose > 0)
2680 {
2681 if (sourcing_name == NULL)
2682 msg_str((char_u *)_("could not source \"%s\""), fname);
2683 else
2684 smsg((char_u *)_("line %ld: could not source \"%s\""),
2685 sourcing_lnum, fname);
2686 }
2687 goto theend;
2688 }
2689
2690 /*
2691 * The file exists.
2692 * - In verbose mode, give a message.
2693 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
2694 */
2695 if (p_verbose > 1)
2696 {
2697 if (sourcing_name == NULL)
2698 msg_str((char_u *)_("sourcing \"%s\""), fname);
2699 else
2700 smsg((char_u *)_("line %ld: sourcing \"%s\""),
2701 sourcing_lnum, fname);
2702 }
2703 if (is_vimrc)
2704 vimrc_found();
2705
2706#ifdef USE_CRNL
2707 /* If no automatic file format: Set default to CR-NL. */
2708 if (*p_ffs == NUL)
2709 cookie.fileformat = EOL_DOS;
2710 else
2711 cookie.fileformat = EOL_UNKNOWN;
2712 cookie.error = FALSE;
2713#endif
2714
2715#ifdef USE_CR
2716 /* If no automatic file format: Set default to CR. */
2717 if (*p_ffs == NUL)
2718 cookie.fileformat = EOL_MAC;
2719 else
2720 cookie.fileformat = EOL_UNKNOWN;
2721 cookie.error = FALSE;
2722#endif
2723
2724 cookie.nextline = NULL;
2725 cookie.finished = FALSE;
2726
2727#ifdef FEAT_EVAL
2728 /*
2729 * Check if this script has a breakpoint.
2730 */
2731 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
2732 cookie.fname = fname_exp;
2733 cookie.dbg_tick = debug_tick;
2734
2735 cookie.level = ex_nesting_level;
2736#endif
2737#ifdef FEAT_MBYTE
2738 cookie.conv.vc_type = CONV_NONE; /* no conversion */
2739
2740 /* Try reading the first few bytes to check for a UTF-8 BOM. */
2741 {
2742 char_u buf[3];
2743
2744 if (fread((char *)buf, sizeof(char_u), (size_t)3, cookie.fp)
2745 == (size_t)3
2746 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
2747 /* Found BOM, setup conversion and skip over it. */
2748 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
2749 else
2750 /* No BOM found, rewind. */
2751 fseek(cookie.fp, 0L, SEEK_SET);
2752 }
2753#endif
2754
2755 /*
2756 * Keep the sourcing name/lnum, for recursive calls.
2757 */
2758 save_sourcing_name = sourcing_name;
2759 sourcing_name = fname_exp;
2760 save_sourcing_lnum = sourcing_lnum;
2761 sourcing_lnum = 0;
2762
2763#ifdef STARTUPTIME
2764 time_push(&tv_rel, &tv_start);
2765#endif
2766
2767#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00002768# ifdef FEAT_PROFILE
2769 if (do_profiling)
2770 prof_child_enter(&wait_start); /* entering a child now */
2771# endif
2772
2773 /* Don't use local function variables, if called from a function.
2774 * Also starts profiling timer for nested script. */
2775 save_funccalp = save_funccal();
2776
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 /*
2778 * Check if this script was sourced before to finds its SID.
2779 * If it's new, generate a new SID.
2780 */
2781 save_current_SID = current_SID;
2782# ifdef UNIX
2783 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
2784# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002785 for (current_SID = script_items.ga_len; current_SID > 0; --current_SID)
2786 {
2787 si = &SCRIPT_ITEM(current_SID);
2788 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789 && (
2790# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00002791 /* Compare dev/ino when possible, it catches symbolic
2792 * links. Also compare file names, the inode may change
2793 * when the file was edited. */
Bram Moolenaar05159a02005-02-26 23:04:13 +00002794 ((stat_ok && si->sn_dev != -1)
2795 && (si->sn_dev == st.st_dev
2796 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002798 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002800 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002801 if (current_SID == 0)
2802 {
2803 current_SID = ++last_current_SID;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002804 if (ga_grow(&script_items, (int)(current_SID - script_items.ga_len))
2805 == FAIL)
2806 goto almosttheend;
2807 while (script_items.ga_len < current_SID)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00002809 ++script_items.ga_len;
2810 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
2811# ifdef FEAT_PROFILE
2812 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002814 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00002815 si = &SCRIPT_ITEM(current_SID);
2816 si->sn_name = fname_exp;
2817 fname_exp = NULL;
2818# ifdef UNIX
2819 if (stat_ok)
2820 {
2821 si->sn_dev = st.st_dev;
2822 si->sn_ino = st.st_ino;
2823 }
2824 else
2825 si->sn_dev = -1;
2826# endif
2827
Bram Moolenaar071d4272004-06-13 20:20:40 +00002828 /* Allocate the local script variables to use for this script. */
2829 new_script_vars(current_SID);
2830 }
2831
Bram Moolenaar05159a02005-02-26 23:04:13 +00002832# ifdef FEAT_PROFILE
2833 if (do_profiling)
2834 {
2835 int forceit;
2836
2837 /* Check if we do profiling for this script. */
2838 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
2839 {
2840 script_do_profile(si);
2841 si->sn_pr_force = forceit;
2842 }
2843 if (si->sn_prof_on)
2844 {
2845 ++si->sn_pr_count;
2846 profile_start(&si->sn_pr_start);
2847 profile_zero(&si->sn_pr_children);
2848 }
2849 }
2850# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851#endif
2852
2853 /*
2854 * Call do_cmdline, which will call getsourceline() to get the lines.
2855 */
2856 do_cmdline(NULL, getsourceline, (void *)&cookie,
2857 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2858
2859 retval = OK;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002860
2861#ifdef FEAT_PROFILE
2862 if (do_profiling)
2863 {
2864 /* Get "si" again, "script_items" may have been reallocated. */
2865 si = &SCRIPT_ITEM(current_SID);
2866 if (si->sn_prof_on)
2867 {
2868 profile_end(&si->sn_pr_start);
2869 profile_sub_wait(&wait_start, &si->sn_pr_start);
2870 profile_add(&si->sn_pr_total, &si->sn_pr_start);
2871 profile_add(&si->sn_pr_self, &si->sn_pr_start);
2872 profile_sub(&si->sn_pr_self, &si->sn_pr_children);
2873 }
2874 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875#endif
2876
2877 if (got_int)
2878 EMSG(_(e_interr));
2879 sourcing_name = save_sourcing_name;
2880 sourcing_lnum = save_sourcing_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 if (p_verbose > 1)
2882 {
2883 msg_str((char_u *)_("finished sourcing %s"), fname);
2884 if (sourcing_name != NULL)
2885 msg_str((char_u *)_("continuing in %s"), sourcing_name);
2886 }
2887#ifdef STARTUPTIME
2888# ifdef HAVE_SNPRINTF
2889 snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2890# else
2891 sprintf(IObuff, "sourcing %s", fname);
2892# endif
2893 time_msg(IObuff, &tv_start);
2894 time_pop(&tv_rel);
2895#endif
2896
2897#ifdef FEAT_EVAL
2898 /*
2899 * After a "finish" in debug mode, need to break at first command of next
2900 * sourced file.
2901 */
2902 if (save_debug_break_level > ex_nesting_level
2903 && debug_break_level == ex_nesting_level)
2904 ++debug_break_level;
2905#endif
2906
Bram Moolenaar05159a02005-02-26 23:04:13 +00002907#ifdef FEAT_EVAL
2908almosttheend:
2909 current_SID = save_current_SID;
2910 restore_funccal(save_funccalp);
2911# ifdef FEAT_PROFILE
2912 if (do_profiling)
2913 prof_child_exit(&wait_start); /* leaving a child now */
2914# endif
2915#endif
2916 fclose(cookie.fp);
2917 vim_free(cookie.nextline);
2918#ifdef FEAT_MBYTE
2919 convert_setup(&cookie.conv, NULL, NULL);
2920#endif
2921
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922theend:
2923 vim_free(fname_exp);
2924 return retval;
2925}
2926
2927#if defined(FEAT_EVAL) || defined(PROTO)
2928/*
2929 * ":scriptnames"
2930 */
2931/*ARGSUSED*/
2932 void
2933ex_scriptnames(eap)
2934 exarg_T *eap;
2935{
2936 int i;
2937
Bram Moolenaar05159a02005-02-26 23:04:13 +00002938 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
2939 if (SCRIPT_ITEM(i).sn_name != NULL)
2940 smsg((char_u *)"%3d: %s", i, SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941}
2942
2943# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2944/*
2945 * Fix slashes in the list of script names for 'shellslash'.
2946 */
2947 void
2948scriptnames_slash_adjust()
2949{
2950 int i;
2951
Bram Moolenaar05159a02005-02-26 23:04:13 +00002952 for (i = 1; i <= script_items.ga_len; ++i)
2953 if (SCRIPT_ITEM(i).sn_name != NULL)
2954 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955}
2956# endif
2957
2958/*
2959 * Get a pointer to a script name. Used for ":verbose set".
2960 */
2961 char_u *
2962get_scriptname(id)
2963 scid_T id;
2964{
2965 if (id == SID_MODELINE)
2966 return (char_u *)"modeline";
2967 if (id == SID_CMDARG)
2968 return (char_u *)"--cmd argument";
2969 if (id == SID_CARG)
2970 return (char_u *)"-c argument";
2971 if (id == SID_ENV)
2972 return (char_u *)"environment variable";
Bram Moolenaar05159a02005-02-26 23:04:13 +00002973 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974}
Bram Moolenaar05159a02005-02-26 23:04:13 +00002975
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976#endif
2977
2978#if defined(USE_CR) || defined(PROTO)
2979
2980# if defined(__MSL__) && (__MSL__ >= 22)
2981/*
2982 * Newer version of the Metrowerks library handle DOS and UNIX files
2983 * without help.
2984 * Test with earlier versions, MSL 2.2 is the library supplied with
2985 * Codewarrior Pro 2.
2986 */
2987 char *
2988fgets_cr(s, n, stream)
2989 char *s;
2990 int n;
2991 FILE *stream;
2992{
2993 return fgets(s, n, stream);
2994}
2995# else
2996/*
2997 * Version of fgets() which also works for lines ending in a <CR> only
2998 * (Macintosh format).
2999 * For older versions of the Metrowerks library.
3000 * At least CodeWarrior 9 needed this code.
3001 */
3002 char *
3003fgets_cr(s, n, stream)
3004 char *s;
3005 int n;
3006 FILE *stream;
3007{
3008 int c = 0;
3009 int char_read = 0;
3010
3011 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
3012 {
3013 c = fgetc(stream);
3014 s[char_read++] = c;
3015 /* If the file is in DOS format, we need to skip a NL after a CR. I
3016 * thought it was the other way around, but this appears to work... */
3017 if (c == '\n')
3018 {
3019 c = fgetc(stream);
3020 if (c != '\r')
3021 ungetc(c, stream);
3022 }
3023 }
3024
3025 s[char_read] = 0;
3026 if (char_read == 0)
3027 return NULL;
3028
3029 if (feof(stream) && char_read == 1)
3030 return NULL;
3031
3032 return s;
3033}
3034# endif
3035#endif
3036
3037/*
3038 * Get one full line from a sourced file.
3039 * Called by do_cmdline() when it's called from do_source().
3040 *
3041 * Return a pointer to the line in allocated memory.
3042 * Return NULL for end-of-file or some error.
3043 */
3044/* ARGSUSED */
3045 char_u *
3046getsourceline(c, cookie, indent)
3047 int c; /* not used */
3048 void *cookie;
3049 int indent; /* not used */
3050{
3051 struct source_cookie *sp = (struct source_cookie *)cookie;
3052 char_u *line;
3053 char_u *p, *s;
3054
3055#ifdef FEAT_EVAL
3056 /* If breakpoints have been added/deleted need to check for it. */
3057 if (sp->dbg_tick < debug_tick)
3058 {
3059 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3060 sp->dbg_tick = debug_tick;
3061 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00003062# ifdef FEAT_PROFILE
3063 if (do_profiling)
3064 script_line_end();
3065# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066#endif
3067 /*
3068 * Get current line. If there is a read-ahead line, use it, otherwise get
3069 * one now.
3070 */
3071 if (sp->finished)
3072 line = NULL;
3073 else if (sp->nextline == NULL)
3074 line = get_one_sourceline(sp);
3075 else
3076 {
3077 line = sp->nextline;
3078 sp->nextline = NULL;
3079 ++sourcing_lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003080#ifdef FEAT_PROFILE
3081 if (do_profiling)
3082 script_line_start();
3083#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 }
3085
3086 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
3087 * contain the 'C' flag. */
3088 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
3089 {
3090 /* compensate for the one line read-ahead */
3091 --sourcing_lnum;
3092 for (;;)
3093 {
3094 sp->nextline = get_one_sourceline(sp);
3095 if (sp->nextline == NULL)
3096 break;
3097 p = skipwhite(sp->nextline);
3098 if (*p != '\\')
3099 break;
3100 s = alloc((int)(STRLEN(line) + STRLEN(p)));
3101 if (s == NULL) /* out of memory */
3102 break;
3103 STRCPY(s, line);
3104 STRCAT(s, p + 1);
3105 vim_free(line);
3106 line = s;
3107 vim_free(sp->nextline);
3108 }
3109 }
3110
3111#ifdef FEAT_MBYTE
3112 if (line != NULL && sp->conv.vc_type != CONV_NONE)
3113 {
3114 /* Convert the encoding of the script line. */
3115 s = string_convert(&sp->conv, line, NULL);
3116 if (s != NULL)
3117 {
3118 vim_free(line);
3119 line = s;
3120 }
3121 }
3122#endif
3123
3124#ifdef FEAT_EVAL
3125 /* Did we encounter a breakpoint? */
3126 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
3127 {
3128 dbg_breakpoint(sp->fname, sourcing_lnum);
3129 /* Find next breakpoint. */
3130 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3131 sp->dbg_tick = debug_tick;
3132 }
3133#endif
3134
3135 return line;
3136}
3137
3138 static char_u *
3139get_one_sourceline(sp)
3140 struct source_cookie *sp;
3141{
3142 garray_T ga;
3143 int len;
3144 int c;
3145 char_u *buf;
3146#ifdef USE_CRNL
3147 int has_cr; /* CR-LF found */
3148#endif
3149#ifdef USE_CR
3150 char_u *scan;
3151#endif
3152 int have_read = FALSE;
3153
3154 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003155 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156
3157 /*
3158 * Loop until there is a finished line (or end-of-file).
3159 */
3160 sourcing_lnum++;
3161 for (;;)
3162 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003163 /* make room to read at least 120 (more) characters */
3164 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165 break;
3166 buf = (char_u *)ga.ga_data;
3167
3168#ifdef USE_CR
3169 if (sp->fileformat == EOL_MAC)
3170 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00003171 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3172 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173 break;
3174 }
3175 else
3176#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00003177 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3178 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003179 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003180 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003181#ifdef USE_CRNL
3182 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
3183 * CTRL-Z by its own, or after a NL. */
3184 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
3185 && sp->fileformat == EOL_DOS
3186 && buf[len - 1] == Ctrl_Z)
3187 {
3188 buf[len - 1] = NUL;
3189 break;
3190 }
3191#endif
3192
3193#ifdef USE_CR
3194 /* If the read doesn't stop on a new line, and there's
3195 * some CR then we assume a Mac format */
3196 if (sp->fileformat == EOL_UNKNOWN)
3197 {
3198 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
3199 sp->fileformat = EOL_MAC;
3200 else
3201 sp->fileformat = EOL_UNIX;
3202 }
3203
3204 if (sp->fileformat == EOL_MAC)
3205 {
3206 scan = vim_strchr(buf, '\r');
3207
3208 if (scan != NULL)
3209 {
3210 *scan = '\n';
3211 if (*(scan + 1) != 0)
3212 {
3213 *(scan + 1) = 0;
3214 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
3215 }
3216 }
3217 len = STRLEN(buf);
3218 }
3219#endif
3220
3221 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222 ga.ga_len = len;
3223
3224 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00003225 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003226 continue;
3227
3228 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
3229 {
3230#ifdef USE_CRNL
3231 has_cr = (len >= 2 && buf[len - 2] == '\r');
3232 if (sp->fileformat == EOL_UNKNOWN)
3233 {
3234 if (has_cr)
3235 sp->fileformat = EOL_DOS;
3236 else
3237 sp->fileformat = EOL_UNIX;
3238 }
3239
3240 if (sp->fileformat == EOL_DOS)
3241 {
3242 if (has_cr) /* replace trailing CR */
3243 {
3244 buf[len - 2] = '\n';
3245 --len;
3246 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 }
3248 else /* lines like ":map xx yy^M" will have failed */
3249 {
3250 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003251 {
3252 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003254 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255 sp->error = TRUE;
3256 sp->fileformat = EOL_UNIX;
3257 }
3258 }
3259#endif
3260 /* The '\n' is escaped if there is an odd number of ^V's just
3261 * before it, first set "c" just before the 'V's and then check
3262 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
3263 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
3264 ;
3265 if ((len & 1) != (c & 1)) /* escaped NL, read more */
3266 {
3267 sourcing_lnum++;
3268 continue;
3269 }
3270
3271 buf[len - 1] = NUL; /* remove the NL */
3272 }
3273
3274 /*
3275 * Check for ^C here now and then, so recursive :so can be broken.
3276 */
3277 line_breakcheck();
3278 break;
3279 }
3280
3281 if (have_read)
3282 return (char_u *)ga.ga_data;
3283
3284 vim_free(ga.ga_data);
3285 return NULL;
3286}
3287
Bram Moolenaar05159a02005-02-26 23:04:13 +00003288#if defined(FEAT_PROFILE) || defined(PROTO)
3289/*
3290 * Called when starting to read a script line.
3291 * "sourcing_lnum" must be correct!
3292 * When skipping lines it may not actually be executed, but we won't find out
3293 * until later and we need to store the time now.
3294 */
3295 void
3296script_line_start()
3297{
3298 scriptitem_T *si;
3299 sn_prl_T *pp;
3300
3301 if (current_SID <= 0 || current_SID > script_items.ga_len)
3302 return;
3303 si = &SCRIPT_ITEM(current_SID);
3304 if (si->sn_prof_on && sourcing_lnum >= 1)
3305 {
3306 /* Grow the array before starting the timer, so that the time spend
3307 * here isn't counted. */
3308 ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
3309 si->sn_prl_idx = sourcing_lnum - 1;
3310 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
3311 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
3312 {
3313 /* Zero counters for a line that was not used before. */
3314 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
3315 pp->snp_count = 0;
3316 profile_zero(&pp->sn_prl_total);
3317 profile_zero(&pp->sn_prl_self);
3318 ++si->sn_prl_ga.ga_len;
3319 }
3320 si->sn_prl_execed = FALSE;
3321 profile_start(&si->sn_prl_start);
3322 profile_zero(&si->sn_prl_children);
3323 profile_get_wait(&si->sn_prl_wait);
3324 }
3325}
3326
3327/*
3328 * Called when actually executing a function line.
3329 */
3330 void
3331script_line_exec()
3332{
3333 scriptitem_T *si;
3334
3335 if (current_SID <= 0 || current_SID > script_items.ga_len)
3336 return;
3337 si = &SCRIPT_ITEM(current_SID);
3338 if (si->sn_prof_on && si->sn_prl_idx >= 0)
3339 si->sn_prl_execed = TRUE;
3340}
3341
3342/*
3343 * Called when done with a function line.
3344 */
3345 void
3346script_line_end()
3347{
3348 scriptitem_T *si;
3349 sn_prl_T *pp;
3350
3351 if (current_SID <= 0 || current_SID > script_items.ga_len)
3352 return;
3353 si = &SCRIPT_ITEM(current_SID);
3354 if (si->sn_prof_on && si->sn_prl_idx >= 0
3355 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
3356 {
3357 if (si->sn_prl_execed)
3358 {
3359 pp = &PRL_ITEM(si, si->sn_prl_idx);
3360 ++pp->snp_count;
3361 profile_end(&si->sn_prl_start);
3362 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
3363 profile_add(&pp->sn_prl_self, &si->sn_prl_start);
3364 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
3365 profile_sub(&pp->sn_prl_self, &si->sn_prl_children);
3366 }
3367 si->sn_prl_idx = -1;
3368 }
3369}
3370#endif
3371
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372/*
3373 * ":scriptencoding": Set encoding conversion for a sourced script.
3374 * Without the multi-byte feature it's simply ignored.
3375 */
3376/*ARGSUSED*/
3377 void
3378ex_scriptencoding(eap)
3379 exarg_T *eap;
3380{
3381#ifdef FEAT_MBYTE
3382 struct source_cookie *sp;
3383 char_u *name;
3384
3385 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
3386 {
3387 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
3388 return;
3389 }
3390
3391 if (*eap->arg != NUL)
3392 {
3393 name = enc_canonize(eap->arg);
3394 if (name == NULL) /* out of memory */
3395 return;
3396 }
3397 else
3398 name = eap->arg;
3399
3400 /* Setup for conversion from the specified encoding to 'encoding'. */
3401 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
3402 convert_setup(&sp->conv, name, p_enc);
3403
3404 if (name != eap->arg)
3405 vim_free(name);
3406#endif
3407}
3408
3409#if defined(FEAT_EVAL) || defined(PROTO)
3410/*
3411 * ":finish": Mark a sourced file as finished.
3412 */
3413 void
3414ex_finish(eap)
3415 exarg_T *eap;
3416{
3417 if (getline_equal(eap->getline, eap->cookie, getsourceline))
3418 do_finish(eap, FALSE);
3419 else
3420 EMSG(_("E168: :finish used outside of a sourced file"));
3421}
3422
3423/*
3424 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
3425 * Also called for a pending finish at the ":endtry" or after returning from
3426 * an extra do_cmdline(). "reanimate" is used in the latter case.
3427 */
3428 void
3429do_finish(eap, reanimate)
3430 exarg_T *eap;
3431 int reanimate;
3432{
3433 int idx;
3434
3435 if (reanimate)
3436 ((struct source_cookie *)getline_cookie(eap->getline,
3437 eap->cookie))->finished = FALSE;
3438
3439 /*
3440 * Cleanup (and inactivate) conditionals, but stop when a try conditional
3441 * not in its finally clause (which then is to be executed next) is found.
3442 * In this case, make the ":finish" pending for execution at the ":endtry".
3443 * Otherwise, finish normally.
3444 */
3445 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
3446 if (idx >= 0)
3447 {
3448 eap->cstack->cs_pending[idx] = CSTP_FINISH;
3449 report_make_pending(CSTP_FINISH, NULL);
3450 }
3451 else
3452 ((struct source_cookie *)getline_cookie(eap->getline,
3453 eap->cookie))->finished = TRUE;
3454}
3455
3456
3457/*
3458 * Return TRUE when a sourced file had the ":finish" command: Don't give error
3459 * message for missing ":endif".
3460 * Return FALSE when not sourcing a file.
3461 */
3462 int
3463source_finished(getline, cookie)
3464 char_u *(*getline) __ARGS((int, void *, int));
3465 void *cookie;
3466{
3467 return (getline_equal(getline, cookie, getsourceline)
3468 && ((struct source_cookie *)getline_cookie(
3469 getline, cookie))->finished);
3470}
3471#endif
3472
3473#if defined(FEAT_LISTCMDS) || defined(PROTO)
3474/*
3475 * ":checktime [buffer]"
3476 */
3477 void
3478ex_checktime(eap)
3479 exarg_T *eap;
3480{
3481 buf_T *buf;
3482 int save_no_check_timestamps = no_check_timestamps;
3483
3484 no_check_timestamps = 0;
3485 if (eap->addr_count == 0) /* default is all buffers */
3486 check_timestamps(FALSE);
3487 else
3488 {
3489 buf = buflist_findnr((int)eap->line2);
3490 if (buf != NULL) /* cannot happen? */
3491 (void)buf_check_timestamp(buf, FALSE);
3492 }
3493 no_check_timestamps = save_no_check_timestamps;
3494}
3495#endif
3496
3497#if defined(FEAT_PRINTER) || defined(PROTO)
3498/*
3499 * Printing code (Machine-independent.)
3500 * To implement printing on a platform, the following functions must be
3501 * defined:
3502 *
3503 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
3504 * Called once. Code should display printer dialogue (if appropriate) and
3505 * determine printer font and margin settings. Reset has_color if the printer
3506 * doesn't support colors at all.
3507 * Returns FAIL to abort.
3508 *
3509 * int mch_print_begin(prt_settings_T *settings)
3510 * Called to start the print job.
3511 * Return FALSE to abort.
3512 *
3513 * int mch_print_begin_page(char_u *msg)
3514 * Called at the start of each page.
3515 * "msg" indicates the progress of the print job, can be NULL.
3516 * Return FALSE to abort.
3517 *
3518 * int mch_print_end_page()
3519 * Called at the end of each page.
3520 * Return FALSE to abort.
3521 *
3522 * int mch_print_blank_page()
3523 * Called to generate a blank page for collated, duplex, multiple copy
3524 * document. Return FALSE to abort.
3525 *
3526 * void mch_print_end(prt_settings_T *psettings)
3527 * Called at normal end of print job.
3528 *
3529 * void mch_print_cleanup()
3530 * Called if print job ends normally or is abandoned. Free any memory, close
3531 * devices and handles. Also called when mch_print_begin() fails, but not
3532 * when mch_print_init() fails.
3533 *
3534 * void mch_print_set_font(int Bold, int Italic, int Underline);
3535 * Called whenever the font style changes.
3536 *
3537 * void mch_print_set_bg(long bgcol);
3538 * Called to set the background color for the following text. Parameter is an
3539 * RGB value.
3540 *
3541 * void mch_print_set_fg(long fgcol);
3542 * Called to set the foreground color for the following text. Parameter is an
3543 * RGB value.
3544 *
3545 * mch_print_start_line(int margin, int page_line)
3546 * Sets the current position at the start of line "page_line".
3547 * If margin is TRUE start in the left margin (for header and line number).
3548 *
3549 * int mch_print_text_out(char_u *p, int len);
3550 * Output one character of text p[len] at the current position.
3551 * Return TRUE if there is no room for another character in the same line.
3552 *
3553 * Note that the generic code has no idea of margins. The machine code should
3554 * simply make the page look smaller! The header and the line numbers are
3555 * printed in the margin.
3556 */
3557
3558#ifdef FEAT_SYN_HL
3559static const long_u cterm_color_8[8] =
3560{
3561 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
3562 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
3563};
3564
3565static const long_u cterm_color_16[16] =
3566{
3567 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
3568 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
3569 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
3570 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
3571};
3572
3573static int current_syn_id;
3574#endif
3575
3576#define PRCOLOR_BLACK (long_u)0
3577#define PRCOLOR_WHITE (long_u)0xFFFFFFL
3578
3579static int curr_italic;
3580static int curr_bold;
3581static int curr_underline;
3582static long_u curr_bg;
3583static long_u curr_fg;
3584static int page_count;
3585
3586/*
3587 * These values determine the print position on a page.
3588 */
3589typedef struct
3590{
3591 int lead_spaces; /* remaining spaces for a TAB */
3592 int print_pos; /* virtual column for computing TABs */
3593 colnr_T column; /* byte column */
3594 linenr_T file_line; /* line nr in the buffer */
3595 long_u bytes_printed; /* bytes printed so far */
3596 int ff; /* seen form feed character */
3597} prt_pos_T;
3598
3599#ifdef FEAT_SYN_HL
3600static long_u darken_rgb __ARGS((long_u rgb));
3601static long_u prt_get_term_color __ARGS((int colorindex));
3602#endif
3603static void prt_set_fg __ARGS((long_u fg));
3604static void prt_set_bg __ARGS((long_u bg));
3605static void prt_set_font __ARGS((int bold, int italic, int underline));
3606static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3607static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3608static void prt_message __ARGS((char_u *s));
3609static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3610static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3611
3612#ifdef FEAT_SYN_HL
3613/*
3614 * If using a dark background, the colors will probably be too bright to show
3615 * up well on white paper, so reduce their brightness.
3616 */
3617 static long_u
3618darken_rgb(rgb)
3619 long_u rgb;
3620{
3621 return ((rgb >> 17) << 16)
3622 + (((rgb & 0xff00) >> 9) << 8)
3623 + ((rgb & 0xff) >> 1);
3624}
3625
3626 static long_u
3627prt_get_term_color(colorindex)
3628 int colorindex;
3629{
3630 /* TODO: Should check for xterm with 88 or 256 colors. */
3631 if (t_colors > 8)
3632 return cterm_color_16[colorindex % 16];
3633 return cterm_color_8[colorindex % 8];
3634}
3635
3636 static void
3637prt_get_attr(hl_id, pattr, modec)
3638 int hl_id;
3639 prt_text_attr_T* pattr;
3640 int modec;
3641{
3642 int colorindex;
3643 long_u fg_color;
3644 long_u bg_color;
3645 char *color;
3646
3647 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3648 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3649 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
3650
3651# ifdef FEAT_GUI
3652 if (gui.in_use)
3653 {
3654 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3655 if (bg_color == PRCOLOR_BLACK)
3656 bg_color = PRCOLOR_WHITE;
3657
3658 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3659 }
3660 else
3661# endif
3662 {
3663 bg_color = PRCOLOR_WHITE;
3664
3665 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3666 if (color == NULL)
3667 colorindex = 0;
3668 else
3669 colorindex = atoi(color);
3670
3671 if (colorindex >= 0 && colorindex < t_colors)
3672 fg_color = prt_get_term_color(colorindex);
3673 else
3674 fg_color = PRCOLOR_BLACK;
3675 }
3676
3677 if (fg_color == PRCOLOR_WHITE)
3678 fg_color = PRCOLOR_BLACK;
3679 else if (*p_bg == 'd')
3680 fg_color = darken_rgb(fg_color);
3681
3682 pattr->fg_color = fg_color;
3683 pattr->bg_color = bg_color;
3684}
3685#endif /* FEAT_SYN_HL */
3686
3687 static void
3688prt_set_fg(fg)
3689 long_u fg;
3690{
3691 if (fg != curr_fg)
3692 {
3693 curr_fg = fg;
3694 mch_print_set_fg(fg);
3695 }
3696}
3697
3698 static void
3699prt_set_bg(bg)
3700 long_u bg;
3701{
3702 if (bg != curr_bg)
3703 {
3704 curr_bg = bg;
3705 mch_print_set_bg(bg);
3706 }
3707}
3708
3709 static void
3710prt_set_font(bold, italic, underline)
3711 int bold;
3712 int italic;
3713 int underline;
3714{
3715 if (curr_bold != bold
3716 || curr_italic != italic
3717 || curr_underline != underline)
3718 {
3719 curr_underline = underline;
3720 curr_italic = italic;
3721 curr_bold = bold;
3722 mch_print_set_font(bold, italic, underline);
3723 }
3724}
3725
3726/*
3727 * Print the line number in the left margin.
3728 */
3729 static void
3730prt_line_number(psettings, page_line, lnum)
3731 prt_settings_T *psettings;
3732 int page_line;
3733 linenr_T lnum;
3734{
3735 int i;
3736 char_u tbuf[20];
3737
3738 prt_set_fg(psettings->number.fg_color);
3739 prt_set_bg(psettings->number.bg_color);
3740 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3741 mch_print_start_line(TRUE, page_line);
3742
3743 /* Leave two spaces between the number and the text; depends on
3744 * PRINT_NUMBER_WIDTH. */
3745 sprintf((char *)tbuf, "%6ld", (long)lnum);
3746 for (i = 0; i < 6; i++)
3747 (void)mch_print_text_out(&tbuf[i], 1);
3748
3749#ifdef FEAT_SYN_HL
3750 if (psettings->do_syntax)
3751 /* Set colors for next character. */
3752 current_syn_id = -1;
3753 else
3754#endif
3755 {
3756 /* Set colors and font back to normal. */
3757 prt_set_fg(PRCOLOR_BLACK);
3758 prt_set_bg(PRCOLOR_WHITE);
3759 prt_set_font(FALSE, FALSE, FALSE);
3760 }
3761}
3762
3763static linenr_T printer_page_num;
3764
3765 int
3766get_printer_page_num()
3767{
3768 return printer_page_num;
3769}
3770
3771/*
3772 * Get the currently effective header height.
3773 */
3774 int
3775prt_header_height()
3776{
3777 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3778 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3779 return 2;
3780}
3781
3782/*
3783 * Return TRUE if using a line number for printing.
3784 */
3785 int
3786prt_use_number()
3787{
3788 return (printer_opts[OPT_PRINT_NUMBER].present
3789 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3790}
3791
3792/*
3793 * Return the unit used in a margin item in 'printoptions'.
3794 * Returns PRT_UNIT_NONE if not recognized.
3795 */
3796 int
3797prt_get_unit(idx)
3798 int idx;
3799{
3800 int u = PRT_UNIT_NONE;
3801 int i;
3802 static char *(units[4]) = PRT_UNIT_NAMES;
3803
3804 if (printer_opts[idx].present)
3805 for (i = 0; i < 4; ++i)
3806 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3807 {
3808 u = i;
3809 break;
3810 }
3811 return u;
3812}
3813
3814/*
3815 * Print the page header.
3816 */
3817/*ARGSUSED*/
3818 static void
3819prt_header(psettings, pagenum, lnum)
3820 prt_settings_T *psettings;
3821 int pagenum;
3822 linenr_T lnum;
3823{
3824 int width = psettings->chars_per_line;
3825 int page_line;
3826 char_u *tbuf;
3827 char_u *p;
3828#ifdef FEAT_MBYTE
3829 int l;
3830#endif
3831
3832 /* Also use the space for the line number. */
3833 if (prt_use_number())
3834 width += PRINT_NUMBER_WIDTH;
3835
3836 tbuf = alloc(width + IOSIZE);
3837 if (tbuf == NULL)
3838 return;
3839
3840#ifdef FEAT_STL_OPT
3841 if (*p_header != NUL)
3842 {
3843 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3844
3845 /*
3846 * Need to (temporarily) set current line number and first/last line
3847 * number on the 'window'. Since we don't know how long the page is,
3848 * set the first and current line number to the top line, and guess
3849 * that the page length is 64.
3850 */
3851 tmp_lnum = curwin->w_cursor.lnum;
3852 tmp_topline = curwin->w_topline;
3853 tmp_botline = curwin->w_botline;
3854 curwin->w_cursor.lnum = lnum;
3855 curwin->w_topline = lnum;
3856 curwin->w_botline = lnum + 63;
3857 printer_page_num = pagenum;
3858
3859 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3860 p_header, ' ', width, NULL);
3861
3862 /* Reset line numbers */
3863 curwin->w_cursor.lnum = tmp_lnum;
3864 curwin->w_topline = tmp_topline;
3865 curwin->w_botline = tmp_botline;
3866 }
3867 else
3868#endif
3869 sprintf((char *)tbuf, _("Page %d"), pagenum);
3870
3871 prt_set_fg(PRCOLOR_BLACK);
3872 prt_set_bg(PRCOLOR_WHITE);
3873 prt_set_font(TRUE, FALSE, FALSE);
3874
3875 /* Use a negative line number to indicate printing in the top margin. */
3876 page_line = 0 - prt_header_height();
3877 mch_print_start_line(TRUE, page_line);
3878 for (p = tbuf; *p != NUL; )
3879 {
3880 if (mch_print_text_out(p,
3881#ifdef FEAT_MBYTE
3882 (l = (*mb_ptr2len_check)(p))
3883#else
3884 1
3885#endif
3886 ))
3887 {
3888 ++page_line;
3889 if (page_line >= 0) /* out of room in header */
3890 break;
3891 mch_print_start_line(TRUE, page_line);
3892 }
3893#ifdef FEAT_MBYTE
3894 p += l;
3895#else
3896 p++;
3897#endif
3898 }
3899
3900 vim_free(tbuf);
3901
3902#ifdef FEAT_SYN_HL
3903 if (psettings->do_syntax)
3904 /* Set colors for next character. */
3905 current_syn_id = -1;
3906 else
3907#endif
3908 {
3909 /* Set colors and font back to normal. */
3910 prt_set_fg(PRCOLOR_BLACK);
3911 prt_set_bg(PRCOLOR_WHITE);
3912 prt_set_font(FALSE, FALSE, FALSE);
3913 }
3914}
3915
3916/*
3917 * Display a print status message.
3918 */
3919 static void
3920prt_message(s)
3921 char_u *s;
3922{
3923 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3924 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3925 out_flush();
3926}
3927
3928 void
3929ex_hardcopy(eap)
3930 exarg_T *eap;
3931{
3932 linenr_T lnum;
3933 int collated_copies, uncollated_copies;
3934 prt_settings_T settings;
3935 long_u bytes_to_print = 0;
3936 int page_line;
3937 int jobsplit;
3938 int id;
3939
3940 memset(&settings, 0, sizeof(prt_settings_T));
3941 settings.has_color = TRUE;
3942
3943# ifdef FEAT_POSTSCRIPT
3944 if (*eap->arg == '>')
3945 {
3946 char_u *errormsg = NULL;
3947
3948 /* Expand things like "%.ps". */
3949 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3950 {
3951 if (errormsg != NULL)
3952 EMSG(errormsg);
3953 return;
3954 }
3955 settings.outfile = skipwhite(eap->arg + 1);
3956 }
3957 else if (*eap->arg != NUL)
3958 settings.arguments = eap->arg;
3959# endif
3960
3961 /*
3962 * Initialise for printing. Ask the user for settings, unless forceit is
3963 * set.
3964 * The mch_print_init() code should set up margins if applicable. (It may
3965 * not be a real printer - for example the engine might generate HTML or
3966 * PS.)
3967 */
3968 if (mch_print_init(&settings,
3969 curbuf->b_fname == NULL
3970 ? (char_u *)buf_spname(curbuf)
3971 : curbuf->b_sfname == NULL
3972 ? curbuf->b_fname
3973 : curbuf->b_sfname,
3974 eap->forceit) == FAIL)
3975 return;
3976
3977#ifdef FEAT_SYN_HL
3978# ifdef FEAT_GUI
3979 if (gui.in_use)
3980 settings.modec = 'g';
3981 else
3982# endif
3983 if (t_colors > 1)
3984 settings.modec = 'c';
3985 else
3986 settings.modec = 't';
3987
3988 if (!syntax_present(curbuf))
3989 settings.do_syntax = FALSE;
3990 else if (printer_opts[OPT_PRINT_SYNTAX].present
3991 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
3992 settings.do_syntax =
3993 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
3994 else
3995 settings.do_syntax = settings.has_color;
3996#endif
3997
3998 /* Set up printing attributes for line numbers */
3999 settings.number.fg_color = PRCOLOR_BLACK;
4000 settings.number.bg_color = PRCOLOR_WHITE;
4001 settings.number.bold = FALSE;
4002 settings.number.italic = TRUE;
4003 settings.number.underline = FALSE;
4004#ifdef FEAT_SYN_HL
4005 /*
4006 * Syntax highlighting of line numbers.
4007 */
4008 if (prt_use_number() && settings.do_syntax)
4009 {
4010 id = syn_name2id((char_u *)"LineNr");
4011 if (id > 0)
4012 id = syn_get_final_id(id);
4013
4014 prt_get_attr(id, &settings.number, settings.modec);
4015 }
4016#endif /* FEAT_SYN_HL */
4017
4018 /*
4019 * Estimate the total lines to be printed
4020 */
4021 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
4022 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
4023 if (bytes_to_print == 0)
4024 {
4025 MSG(_("No text to be printed"));
4026 goto print_fail_no_begin;
4027 }
4028
4029 /* Set colors and font to normal. */
4030 curr_bg = (long_u)0xffffffffL;
4031 curr_fg = (long_u)0xffffffffL;
4032 curr_italic = MAYBE;
4033 curr_bold = MAYBE;
4034 curr_underline = MAYBE;
4035
4036 prt_set_fg(PRCOLOR_BLACK);
4037 prt_set_bg(PRCOLOR_WHITE);
4038 prt_set_font(FALSE, FALSE, FALSE);
4039#ifdef FEAT_SYN_HL
4040 current_syn_id = -1;
4041#endif
4042
4043 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
4044 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
4045
4046 if (!mch_print_begin(&settings))
4047 goto print_fail_no_begin;
4048
4049 /*
4050 * Loop over collated copies: 1 2 3, 1 2 3, ...
4051 */
4052 page_count = 0;
4053 for (collated_copies = 0;
4054 collated_copies < settings.n_collated_copies;
4055 collated_copies++)
4056 {
4057 prt_pos_T prtpos; /* current print position */
4058 prt_pos_T page_prtpos; /* print position at page start */
4059 int side;
4060
4061 memset(&page_prtpos, 0, sizeof(prt_pos_T));
4062 page_prtpos.file_line = eap->line1;
4063 prtpos = page_prtpos;
4064
4065 if (jobsplit && collated_copies > 0)
4066 {
4067 /* Splitting jobs: Stop a previous job and start a new one. */
4068 mch_print_end(&settings);
4069 if (!mch_print_begin(&settings))
4070 goto print_fail_no_begin;
4071 }
4072
4073 /*
4074 * Loop over all pages in the print job: 1 2 3 ...
4075 */
4076 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
4077 {
4078 /*
4079 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
4080 * For duplex: 12 12 12 34 34 34, ...
4081 */
4082 for (uncollated_copies = 0;
4083 uncollated_copies < settings.n_uncollated_copies;
4084 uncollated_copies++)
4085 {
4086 /* Set the print position to the start of this page. */
4087 prtpos = page_prtpos;
4088
4089 /*
4090 * Do front and rear side of a page.
4091 */
4092 for (side = 0; side <= settings.duplex; ++side)
4093 {
4094 /*
4095 * Print one page.
4096 */
4097
4098 /* Check for interrupt character every page. */
4099 ui_breakcheck();
4100 if (got_int || settings.user_abort)
4101 goto print_fail;
4102
4103 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
4104 page_count + 1 + side,
4105 prtpos.bytes_printed > 1000000
4106 ? (int)(prtpos.bytes_printed /
4107 (bytes_to_print / 100))
4108 : (int)((prtpos.bytes_printed * 100)
4109 / bytes_to_print));
4110 if (!mch_print_begin_page(IObuff))
4111 goto print_fail;
4112
4113 if (settings.n_collated_copies > 1)
4114 sprintf((char *)IObuff + STRLEN(IObuff),
4115 _(" Copy %d of %d"),
4116 collated_copies + 1,
4117 settings.n_collated_copies);
4118 prt_message(IObuff);
4119
4120 /*
4121 * Output header if required
4122 */
4123 if (prt_header_height() > 0)
4124 prt_header(&settings, page_count + 1 + side,
4125 prtpos.file_line);
4126
4127 for (page_line = 0; page_line < settings.lines_per_page;
4128 ++page_line)
4129 {
4130 prtpos.column = hardcopy_line(&settings,
4131 page_line, &prtpos);
4132 if (prtpos.column == 0)
4133 {
4134 /* finished a file line */
4135 prtpos.bytes_printed +=
4136 STRLEN(skipwhite(ml_get(prtpos.file_line)));
4137 if (++prtpos.file_line > eap->line2)
4138 break; /* reached the end */
4139 }
4140 else if (prtpos.ff)
4141 {
4142 /* Line had a formfeed in it - start new page but
4143 * stay on the current line */
4144 break;
4145 }
4146 }
4147
4148 if (!mch_print_end_page())
4149 goto print_fail;
4150 if (prtpos.file_line > eap->line2)
4151 break; /* reached the end */
4152 }
4153
4154 /*
4155 * Extra blank page for duplexing with odd number of pages and
4156 * more copies to come.
4157 */
4158 if (prtpos.file_line > eap->line2 && settings.duplex
4159 && side == 0
4160 && uncollated_copies + 1 < settings.n_uncollated_copies)
4161 {
4162 if (!mch_print_blank_page())
4163 goto print_fail;
4164 }
4165 }
4166 if (settings.duplex && prtpos.file_line <= eap->line2)
4167 ++page_count;
4168
4169 /* Remember the position where the next page starts. */
4170 page_prtpos = prtpos;
4171 }
4172
4173 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
4174 prt_message(IObuff);
4175 }
4176
4177print_fail:
4178 if (got_int || settings.user_abort)
4179 {
4180 sprintf((char *)IObuff, _("Printing aborted"));
4181 prt_message(IObuff);
4182 }
4183 mch_print_end(&settings);
4184
4185print_fail_no_begin:
4186 mch_print_cleanup();
4187}
4188
4189/*
4190 * Print one page line.
4191 * Return the next column to print, or zero if the line is finished.
4192 */
4193 static colnr_T
4194hardcopy_line(psettings, page_line, ppos)
4195 prt_settings_T *psettings;
4196 int page_line;
4197 prt_pos_T *ppos;
4198{
4199 colnr_T col;
4200 char_u *line;
4201 int need_break = FALSE;
4202 int outputlen;
4203 int tab_spaces;
4204 long_u print_pos;
4205#ifdef FEAT_SYN_HL
4206 prt_text_attr_T attr;
4207 int id;
4208#endif
4209
4210 if (ppos->column == 0 || ppos->ff)
4211 {
4212 print_pos = 0;
4213 tab_spaces = 0;
4214 if (!ppos->ff && prt_use_number())
4215 prt_line_number(psettings, page_line, ppos->file_line);
4216 ppos->ff = FALSE;
4217 }
4218 else
4219 {
4220 /* left over from wrap halfway a tab */
4221 print_pos = ppos->print_pos;
4222 tab_spaces = ppos->lead_spaces;
4223 }
4224
4225 mch_print_start_line(0, page_line);
4226 line = ml_get(ppos->file_line);
4227
4228 /*
4229 * Loop over the columns until the end of the file line or right margin.
4230 */
4231 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
4232 {
4233 outputlen = 1;
4234#ifdef FEAT_MBYTE
4235 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
4236 outputlen = 1;
4237#endif
4238#ifdef FEAT_SYN_HL
4239 /*
4240 * syntax highlighting stuff.
4241 */
4242 if (psettings->do_syntax)
4243 {
4244 id = syn_get_id(ppos->file_line, (long)col, 1);
4245 if (id > 0)
4246 id = syn_get_final_id(id);
4247 else
4248 id = 0;
4249 /* Get the line again, a multi-line regexp may invalidate it. */
4250 line = ml_get(ppos->file_line);
4251
4252 if (id != current_syn_id)
4253 {
4254 current_syn_id = id;
4255 prt_get_attr(id, &attr, psettings->modec);
4256 prt_set_font(attr.bold, attr.italic, attr.underline);
4257 prt_set_fg(attr.fg_color);
4258 prt_set_bg(attr.bg_color);
4259 }
4260 }
4261#endif /* FEAT_SYN_HL */
4262
4263 /*
4264 * Appropriately expand any tabs to spaces.
4265 */
4266 if (line[col] == TAB || tab_spaces != 0)
4267 {
4268 if (tab_spaces == 0)
4269 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
4270
4271 while (tab_spaces > 0)
4272 {
4273 need_break = mch_print_text_out((char_u *)" ", 1);
4274 print_pos++;
4275 tab_spaces--;
4276 if (need_break)
4277 break;
4278 }
4279 /* Keep the TAB if we didn't finish it. */
4280 if (need_break && tab_spaces > 0)
4281 break;
4282 }
4283 else if (line[col] == FF
4284 && printer_opts[OPT_PRINT_FORMFEED].present
4285 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
4286 == 'y')
4287 {
4288 ppos->ff = TRUE;
4289 need_break = 1;
4290 }
4291 else
4292 {
4293 need_break = mch_print_text_out(line + col, outputlen);
4294#ifdef FEAT_MBYTE
4295 if (has_mbyte)
4296 print_pos += (*mb_ptr2cells)(line + col);
4297 else
4298#endif
4299 print_pos++;
4300 }
4301 }
4302
4303 ppos->lead_spaces = tab_spaces;
4304 ppos->print_pos = print_pos;
4305
4306 /*
4307 * Start next line of file if we clip lines, or have reached end of the
4308 * line, unless we are doing a formfeed.
4309 */
4310 if (!ppos->ff
4311 && (line[col] == NUL
4312 || (printer_opts[OPT_PRINT_WRAP].present
4313 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
4314 == 'n')))
4315 return 0;
4316 return col;
4317}
4318
4319# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
4320
4321/*
4322 * PS printer stuff.
4323 *
4324 * Sources of information to help maintain the PS printing code:
4325 *
4326 * 1. PostScript Language Reference, 3rd Edition,
4327 * Addison-Wesley, 1999, ISBN 0-201-37922-8
4328 * 2. PostScript Language Program Design,
4329 * Addison-Wesley, 1988, ISBN 0-201-14396-8
4330 * 3. PostScript Tutorial and Cookbook,
4331 * Addison Wesley, 1985, ISBN 0-201-10179-3
4332 * 4. PostScript Language Document Structuring Conventions Specification,
4333 * version 3.0,
4334 * Adobe Technote 5001, 25th September 1992
4335 * 5. PostScript Printer Description File Format Specification, Version 4.3,
4336 * Adobe technote 5003, 9th February 1996
4337 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
4338 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00004339 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
4340 * Adobe Technote 5014, 8th October 1996
4341 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
4342 * Adoboe Technote 5094, 8th September, 2001
4343 * 9. CJKV Information Processing, 2nd Edition,
4344 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 *
4346 * Some of these documents can be found in PDF form on Adobe's web site -
4347 * http://www.adobe.com
4348 */
4349
Bram Moolenaar8299df92004-07-10 09:47:34 +00004350#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
4351
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
4353#define PRT_PS_DEFAULT_FONTSIZE (10)
4354#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
4355
4356struct prt_mediasize_S
4357{
4358 char *name;
4359 float width; /* width and height in points for portrait */
4360 float height;
4361};
4362
4363#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
4364
4365static struct prt_mediasize_S prt_mediasize[] =
4366{
4367 {"A4", 595.0, 842.0},
4368 {"letter", 612.0, 792.0},
4369 {"10x14", 720.0, 1008.0},
4370 {"A3", 842.0, 1191.0},
4371 {"A5", 420.0, 595.0},
4372 {"B4", 729.0, 1032.0},
4373 {"B5", 516.0, 729.0},
4374 {"executive", 522.0, 756.0},
4375 {"folio", 595.0, 935.0},
4376 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
4377 {"legal", 612.0, 1008.0},
4378 {"quarto", 610.0, 780.0},
4379 {"statement", 396.0, 612.0},
4380 {"tabloid", 792.0, 1224.0}
4381};
4382
4383/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
4384struct prt_ps_font_S
4385{
4386 int wx;
4387 int uline_offset;
4388 int uline_width;
4389 int bbox_min_y;
4390 int bbox_max_y;
4391 char *(ps_fontname[4]);
4392};
4393
4394#define PRT_PS_FONT_ROMAN (0)
4395#define PRT_PS_FONT_BOLD (1)
4396#define PRT_PS_FONT_OBLIQUE (2)
4397#define PRT_PS_FONT_BOLDOBLIQUE (3)
4398
Bram Moolenaar8299df92004-07-10 09:47:34 +00004399/* Standard font metrics for Courier family */
4400static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401{
4402 600,
4403 -100, 50,
4404 -250, 805,
4405 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
4406};
4407
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004408#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004409/* Generic font metrics for multi-byte fonts */
4410static struct prt_ps_font_S prt_ps_mb_font =
4411{
4412 1000,
4413 -100, 50,
4414 -250, 805,
4415 {NULL, NULL, NULL, NULL}
4416};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004417#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00004418
4419/* Pointer to current font set being used */
4420static struct prt_ps_font_S* prt_ps_font;
4421
4422/* Structures to map user named encoding and mapping to PS equivalents for
4423 * building CID font name */
4424struct prt_ps_encoding_S
4425{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004426 char *encoding;
4427 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004428 int needs_charset;
4429};
4430
4431struct prt_ps_charset_S
4432{
4433 char *charset;
4434 char *cmap_charset;
4435 int has_charset;
4436};
4437
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004438#ifdef FEAT_MBYTE
4439
Bram Moolenaar8299df92004-07-10 09:47:34 +00004440#define CS_JIS_C_1978 (0x01)
4441#define CS_JIS_X_1983 (0x02)
4442#define CS_JIS_X_1990 (0x04)
4443#define CS_NEC (0x08)
4444#define CS_MSWINDOWS (0x10)
4445#define CS_CP932 (0x20)
4446#define CS_KANJITALK6 (0x40)
4447#define CS_KANJITALK7 (0x80)
4448
4449/* Japanese encodings and charsets */
4450static struct prt_ps_encoding_S j_encodings[] =
4451{
4452 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
4453 CS_NEC)},
4454 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
4455 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
4456 CS_KANJITALK6|CS_KANJITALK7)},
4457 {"cp932", "RKSJ", CS_JIS_X_1983},
4458 {"ucs-2", "UCS2", CS_JIS_X_1990},
4459 {"utf-8", "UTF8" , CS_JIS_X_1990}
4460};
4461static struct prt_ps_charset_S j_charsets[] =
4462{
4463 {"JIS_C_1978", "78", CS_JIS_C_1978},
4464 {"JIS_X_1983", NULL, CS_JIS_X_1983},
4465 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
4466 {"NEC", "Ext", CS_NEC},
4467 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
4468 {"CP932", "90ms", CS_JIS_X_1983},
4469 {"KANJITALK6", "83pv", CS_KANJITALK6},
4470 {"KANJITALK7", "90pv", CS_KANJITALK7}
4471};
4472
4473#define CS_GB_2312_80 (0x01)
4474#define CS_GBT_12345_90 (0x02)
4475#define CS_GBK2K (0x04)
4476#define CS_SC_MAC (0x08)
4477#define CS_GBT_90_MAC (0x10)
4478#define CS_GBK (0x20)
4479#define CS_SC_ISO10646 (0x40)
4480
4481/* Simplified Chinese encodings and charsets */
4482static struct prt_ps_encoding_S sc_encodings[] =
4483{
4484 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
4485 {"gb18030", NULL, CS_GBK2K},
4486 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
4487 CS_GBT_90_MAC)},
4488 {"gbk", "EUC", CS_GBK},
4489 {"ucs-2", "UCS2", CS_SC_ISO10646},
4490 {"utf-8", "UTF8", CS_SC_ISO10646}
4491};
4492static struct prt_ps_charset_S sc_charsets[] =
4493{
4494 {"GB_2312-80", "GB", CS_GB_2312_80},
4495 {"GBT_12345-90","GBT", CS_GBT_12345_90},
4496 {"MAC", "GBpc", CS_SC_MAC},
4497 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
4498 {"GBK", "GBK", CS_GBK},
4499 {"GB18030", "GBK2K", CS_GBK2K},
4500 {"ISO10646", "UniGB", CS_SC_ISO10646}
4501};
4502
4503#define CS_CNS_PLANE_1 (0x01)
4504#define CS_CNS_PLANE_2 (0x02)
4505#define CS_CNS_PLANE_1_2 (0x04)
4506#define CS_B5 (0x08)
4507#define CS_ETEN (0x10)
4508#define CS_HK_GCCS (0x20)
4509#define CS_HK_SCS (0x40)
4510#define CS_HK_SCS_ETEN (0x80)
4511#define CS_MTHKL (0x100)
4512#define CS_MTHKS (0x200)
4513#define CS_DLHKL (0x400)
4514#define CS_DLHKS (0x800)
4515#define CS_TC_ISO10646 (0x1000)
4516
4517/* Traditional Chinese encodings and charsets */
4518static struct prt_ps_encoding_S tc_encodings[] =
4519{
4520 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
4521 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
4522 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
4523 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
4524 CS_DLHKS)},
4525 {"cp950", "B5", CS_B5},
4526 {"ucs-2", "UCS2", CS_TC_ISO10646},
4527 {"utf-8", "UTF8", CS_TC_ISO10646},
4528 {"utf-16", "UTF16", CS_TC_ISO10646},
4529 {"utf-32", "UTF32", CS_TC_ISO10646}
4530};
4531static struct prt_ps_charset_S tc_charsets[] =
4532{
4533 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
4534 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
4535 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
4536 {"BIG5", NULL, CS_B5},
4537 {"CP950", NULL, CS_B5},
4538 {"ETEN", "ETen", CS_ETEN},
4539 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
4540 {"SCS", "HKscs", CS_HK_SCS},
4541 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
4542 {"MTHKL", "HKm471", CS_MTHKL},
4543 {"MTHKS", "HKm314", CS_MTHKS},
4544 {"DLHKL", "HKdla", CS_DLHKL},
4545 {"DLHKS", "HKdlb", CS_DLHKS},
4546 {"ISO10646", "UniCNS", CS_TC_ISO10646}
4547};
4548
4549#define CS_KR_X_1992 (0x01)
4550#define CS_KR_MAC (0x02)
4551#define CS_KR_X_1992_MS (0x04)
4552#define CS_KR_ISO10646 (0x08)
4553
4554/* Korean encodings and charsets */
4555static struct prt_ps_encoding_S k_encodings[] =
4556{
4557 {"iso-2022-kr", NULL, CS_KR_X_1992},
4558 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
4559 {"johab", "Johab", CS_KR_X_1992},
4560 {"cp1361", "Johab", CS_KR_X_1992},
4561 {"uhc", "UHC", CS_KR_X_1992_MS},
4562 {"cp949", "UHC", CS_KR_X_1992_MS},
4563 {"ucs-2", "UCS2", CS_KR_ISO10646},
4564 {"utf-8", "UTF8", CS_KR_ISO10646}
4565};
4566static struct prt_ps_charset_S k_charsets[] =
4567{
4568 {"KS_X_1992", "KSC", CS_KR_X_1992},
4569 {"CP1361", "KSC", CS_KR_X_1992},
4570 {"MAC", "KSCpc", CS_KR_MAC},
4571 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
4572 {"CP949", "KSCms", CS_KR_X_1992_MS},
4573 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
4574 {"ISO10646", "UniKS", CS_KR_ISO10646}
4575};
4576
4577/* Collections of encodings and charsets for multi-byte printing */
4578struct prt_ps_mbfont_S
4579{
4580 int num_encodings;
4581 struct prt_ps_encoding_S *encodings;
4582 int num_charsets;
4583 struct prt_ps_charset_S *charsets;
4584 char *ascii_enc;
4585 char *defcs;
4586};
4587
4588static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4589{
4590 {
4591 NUM_ELEMENTS(j_encodings),
4592 j_encodings,
4593 NUM_ELEMENTS(j_charsets),
4594 j_charsets,
4595 "jis_roman",
4596 "JIS_X_1983"
4597 },
4598 {
4599 NUM_ELEMENTS(sc_encodings),
4600 sc_encodings,
4601 NUM_ELEMENTS(sc_charsets),
4602 sc_charsets,
4603 "gb_roman",
4604 "GB_2312-80"
4605 },
4606 {
4607 NUM_ELEMENTS(tc_encodings),
4608 tc_encodings,
4609 NUM_ELEMENTS(tc_charsets),
4610 tc_charsets,
4611 "cns_roman",
4612 "BIG5"
4613 },
4614 {
4615 NUM_ELEMENTS(k_encodings),
4616 k_encodings,
4617 NUM_ELEMENTS(k_charsets),
4618 k_charsets,
4619 "ks_roman",
4620 "KS_X_1992"
4621 }
4622};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004623#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004624
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625struct prt_ps_resource_S
4626{
4627 char_u name[64];
4628 char_u filename[MAXPATHL + 1];
4629 int type;
4630 char_u title[256];
4631 char_u version[256];
4632};
4633
4634/* Types of PS resource file currently used */
4635#define PRT_RESOURCE_TYPE_PROCSET (0)
4636#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004637#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638
4639/* The PS prolog file version number has to match - if the prolog file is
4640 * updated, increment the number in the file and here. Version checking was
4641 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004642 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 * Table of VIM and prolog versions:
4644 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004645 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004647 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004649#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004650#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651
4652/* String versions of PS resource types - indexed by constants above so don't
4653 * re-order!
4654 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004655static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656{
4657 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004658 "encoding",
4659 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660};
4661
4662/* Strings to look for in a PS resource file */
4663#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4664#define PRT_RESOURCE_RESOURCE "Resource-"
4665#define PRT_RESOURCE_PROCSET "ProcSet"
4666#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004667#define PRT_RESOURCE_CMAP "CMap"
4668
4669
4670/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4671 * read more comments. */
4672#define PRT_DSC_MISC_TYPE (-1)
4673#define PRT_DSC_TITLE_TYPE (1)
4674#define PRT_DSC_VERSION_TYPE (2)
4675#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4676
4677#define PRT_DSC_TITLE "%%Title:"
4678#define PRT_DSC_VERSION "%%Version:"
4679#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4680
4681struct prt_dsc_comment_S
4682{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004683 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004684 int len;
4685 int type;
4686};
4687
4688struct prt_dsc_line_S
4689{
4690 int type;
4691 char_u *string;
4692 int len;
4693};
4694
4695
4696#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4697struct prt_dsc_comment_S prt_dsc_table[] =
4698{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004699 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004700 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004701 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004702 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004703 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004704};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705
4706static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4707static void prt_write_file __ARGS((char_u *buffer));
4708static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4709static void prt_write_string __ARGS((char *s));
4710static void prt_write_int __ARGS((int i));
4711static void prt_write_boolean __ARGS((int b));
4712static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4713static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4714static void prt_write_real __ARGS((double val, int prec));
4715static void prt_def_var __ARGS((char *name, double value, int prec));
4716static void prt_flush_buffer __ARGS((void));
4717static void prt_resource_name __ARGS((char_u *filename));
4718static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4719static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4720static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4721static void prt_dsc_start __ARGS((void));
4722static void prt_dsc_noarg __ARGS((char *comment));
4723static void prt_dsc_textline __ARGS((char *comment, char *text));
4724static void prt_dsc_text __ARGS((char *comment, char *text));
4725static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4726static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4727static 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 +00004728static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4729static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730static float to_device_units __ARGS((int idx, double physsize, int def_number));
4731static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4732static void prt_font_metrics __ARGS((int font_scale));
4733static int prt_get_cpl __ARGS((void));
4734static int prt_get_lpp __ARGS((void));
4735static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004736static int prt_resfile_next_line __ARGS((void));
4737static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4738static int prt_resfile_skip_nonws __ARGS((int offset));
4739static int prt_resfile_skip_ws __ARGS((int offset));
4740static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4741#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004742static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004743static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4744static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4745static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4746#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747
4748/*
4749 * Variables for the output PostScript file.
4750 */
4751static FILE *prt_ps_fd;
4752static int prt_file_error;
4753static char_u *prt_ps_file_name = NULL;
4754
4755/*
4756 * Various offsets and dimensions in default PostScript user space (points).
4757 * Used for text positioning calculations
4758 */
4759static float prt_page_width;
4760static float prt_page_height;
4761static float prt_left_margin;
4762static float prt_right_margin;
4763static float prt_top_margin;
4764static float prt_bottom_margin;
4765static float prt_line_height;
4766static float prt_first_line_height;
4767static float prt_char_width;
4768static float prt_number_width;
4769static float prt_bgcol_offset;
4770static float prt_pos_x_moveto = 0.0;
4771static float prt_pos_y_moveto = 0.0;
4772
4773/*
4774 * Various control variables used to decide when and how to change the
4775 * PostScript graphics state.
4776 */
4777static int prt_need_moveto;
4778static int prt_do_moveto;
4779static int prt_need_font;
4780static int prt_font;
4781static int prt_need_underline;
4782static int prt_underline;
4783static int prt_do_underline;
4784static int prt_need_fgcol;
4785static int prt_fgcol;
4786static int prt_need_bgcol;
4787static int prt_do_bgcol;
4788static int prt_bgcol;
4789static int prt_new_bgcol;
4790static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004791static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004793static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794
4795/*
4796 * Variables controlling physical printing.
4797 */
4798static int prt_media;
4799static int prt_portrait;
4800static int prt_num_copies;
4801static int prt_duplex;
4802static int prt_tumble;
4803static int prt_collate;
4804
4805/*
4806 * Buffers used when generating PostScript output
4807 */
4808static char_u prt_line_buffer[257];
4809static garray_T prt_ps_buffer;
4810
4811# ifdef FEAT_MBYTE
4812static int prt_do_conv;
4813static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004814
4815static int prt_out_mbyte;
4816static int prt_custom_cmap;
4817static char prt_cmap[80];
4818static int prt_use_courier;
4819static int prt_in_ascii;
4820static int prt_half_width;
4821static char *prt_ascii_encoding;
4822static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823# endif
4824
4825 static void
4826prt_write_file_raw_len(buffer, bytes)
4827 char_u *buffer;
4828 int bytes;
4829{
4830 if (!prt_file_error
4831 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4832 != (size_t)bytes)
4833 {
4834 EMSG(_("E455: Error writing to PostScript output file"));
4835 prt_file_error = TRUE;
4836 }
4837}
4838
4839 static void
4840prt_write_file(buffer)
4841 char_u *buffer;
4842{
4843 prt_write_file_len(buffer, STRLEN(buffer));
4844}
4845
4846 static void
4847prt_write_file_len(buffer, bytes)
4848 char_u *buffer;
4849 int bytes;
4850{
4851#ifdef EBCDIC
4852 ebcdic2ascii(buffer, bytes);
4853#endif
4854 prt_write_file_raw_len(buffer, bytes);
4855}
4856
4857/*
4858 * Write a string.
4859 */
4860 static void
4861prt_write_string(s)
4862 char *s;
4863{
4864 sprintf((char *)prt_line_buffer, "%s", s);
4865 prt_write_file(prt_line_buffer);
4866}
4867
4868/*
4869 * Write an int and a space.
4870 */
4871 static void
4872prt_write_int(i)
4873 int i;
4874{
4875 sprintf((char *)prt_line_buffer, "%d ", i);
4876 prt_write_file(prt_line_buffer);
4877}
4878
4879/*
4880 * Write a boolean and a space.
4881 */
4882 static void
4883prt_write_boolean(b)
4884 int b;
4885{
4886 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4887 prt_write_file(prt_line_buffer);
4888}
4889
4890/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004891 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 */
4893 static void
4894prt_def_font(new_name, encoding, height, font)
4895 char *new_name;
4896 char *encoding;
4897 int height;
4898 char *font;
4899{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004900 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4901 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004903#ifdef FEAT_MBYTE
4904 if (prt_out_mbyte)
4905 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4906 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4907 else
4908#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4910 new_name, height, new_name);
4911 prt_write_file(prt_line_buffer);
4912}
4913
Bram Moolenaar8299df92004-07-10 09:47:34 +00004914#ifdef FEAT_MBYTE
4915/*
4916 * Write a line to define the CID font.
4917 */
4918 static void
4919prt_def_cidfont(new_name, height, cidfont)
4920 char *new_name;
4921 int height;
4922 char *cidfont;
4923{
4924 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4925 new_name, prt_cmap, cidfont);
4926 prt_write_file(prt_line_buffer);
4927 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4928 new_name);
4929 prt_write_file(prt_line_buffer);
4930}
4931
4932/*
4933 * Write a line to define a duplicate of a CID font
4934 */
4935 static void
4936prt_dup_cidfont(original_name, new_name)
4937 char *original_name;
4938 char *new_name;
4939{
4940 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4941 prt_write_file(prt_line_buffer);
4942}
4943#endif
4944
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945/*
4946 * Convert a real value into an integer and fractional part as integers, with
4947 * the fractional part being in the range [0,10^precision). The fractional part
4948 * is also rounded based on the precision + 1'th fractional digit.
4949 */
4950 static void
4951prt_real_bits(real, precision, pinteger, pfraction)
4952 double real;
4953 int precision;
4954 int *pinteger;
4955 int *pfraction;
4956{
4957 int i;
4958 int integer;
4959 float fraction;
4960
4961 integer = (int)real;
4962 fraction = (float)(real - integer);
4963 if (real < (double)integer)
4964 fraction = -fraction;
4965 for (i = 0; i < precision; i++)
4966 fraction *= 10.0;
4967
4968 *pinteger = integer;
4969 *pfraction = (int)(fraction + 0.5);
4970}
4971
4972/*
4973 * Write a real and a space. Save bytes if real value has no fractional part!
4974 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4975 * what decimal point character to use, but PS always requires a '.'.
4976 */
4977 static void
4978prt_write_real(val, prec)
4979 double val;
4980 int prec;
4981{
4982 int integer;
4983 int fraction;
4984
4985 prt_real_bits(val, prec, &integer, &fraction);
4986 /* Emit integer part */
4987 sprintf((char *)prt_line_buffer, "%d", integer);
4988 prt_write_file(prt_line_buffer);
4989 /* Only emit fraction if necessary */
4990 if (fraction != 0)
4991 {
4992 /* Remove any trailing zeros */
4993 while ((fraction % 10) == 0)
4994 {
4995 prec--;
4996 fraction /= 10;
4997 }
4998 /* Emit fraction left padded with zeros */
4999 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
5000 prt_write_file(prt_line_buffer);
5001 }
5002 sprintf((char *)prt_line_buffer, " ");
5003 prt_write_file(prt_line_buffer);
5004}
5005
5006/*
5007 * Write a line to define a numeric variable.
5008 */
5009 static void
5010prt_def_var(name, value, prec)
5011 char *name;
5012 double value;
5013 int prec;
5014{
5015 sprintf((char *)prt_line_buffer, "/%s ", name);
5016 prt_write_file(prt_line_buffer);
5017 prt_write_real(value, prec);
5018 sprintf((char *)prt_line_buffer, "d\n");
5019 prt_write_file(prt_line_buffer);
5020}
5021
5022/* Convert size from font space to user space at current font scale */
5023#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
5024
5025 static void
5026prt_flush_buffer()
5027{
5028 if (prt_ps_buffer.ga_len > 0)
5029 {
5030 /* Any background color must be drawn first */
5031 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
5032 {
5033 int r, g, b;
5034
5035 if (prt_do_moveto)
5036 {
5037 prt_write_real(prt_pos_x_moveto, 2);
5038 prt_write_real(prt_pos_y_moveto, 2);
5039 prt_write_string("m\n");
5040 prt_do_moveto = FALSE;
5041 }
5042
5043 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005044 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005045 prt_write_real(prt_line_height, 2);
5046
5047 /* Lastly add the color of the background */
5048 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
5049 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
5050 b = prt_new_bgcol & 0xff;
5051 prt_write_real(r / 255.0, 3);
5052 prt_write_real(g / 255.0, 3);
5053 prt_write_real(b / 255.0, 3);
5054 prt_write_string("bg\n");
5055 }
5056 /* Draw underlines before the text as it makes it slightly easier to
5057 * find the starting point.
5058 */
5059 if (prt_do_underline)
5060 {
5061 if (prt_do_moveto)
5062 {
5063 prt_write_real(prt_pos_x_moveto, 2);
5064 prt_write_real(prt_pos_y_moveto, 2);
5065 prt_write_string("m\n");
5066 prt_do_moveto = FALSE;
5067 }
5068
Bram Moolenaar8299df92004-07-10 09:47:34 +00005069 /* Underline length of text run */
5070 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 prt_write_string("ul\n");
5072 }
5073 /* Draw the text
5074 * Note: we write text out raw - EBCDIC conversion is handled in the
5075 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005076#ifdef FEAT_MBYTE
5077 if (prt_out_mbyte)
5078 prt_write_string("<");
5079 else
5080#endif
5081 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005083#ifdef FEAT_MBYTE
5084 if (prt_out_mbyte)
5085 prt_write_string(">");
5086 else
5087#endif
5088 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089 /* Add a moveto if need be and use the appropriate show procedure */
5090 if (prt_do_moveto)
5091 {
5092 prt_write_real(prt_pos_x_moveto, 2);
5093 prt_write_real(prt_pos_y_moveto, 2);
5094 /* moveto and a show */
5095 prt_write_string("ms\n");
5096 prt_do_moveto = FALSE;
5097 }
5098 else /* Simple show */
5099 prt_write_string("s\n");
5100
5101 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005102 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103 }
5104}
5105
5106static char_u *resource_filename;
5107
5108 static void
5109prt_resource_name(filename)
5110 char_u *filename;
5111{
5112 if (STRLEN(filename) >= MAXPATHL)
5113 *resource_filename = NUL;
5114 else
5115 STRCPY(resource_filename, filename);
5116}
5117
5118 static int
5119prt_find_resource(name, resource)
5120 char *name;
5121 struct prt_ps_resource_S *resource;
5122{
5123 char_u buffer[MAXPATHL + 1];
5124
5125 STRCPY(resource->name, name);
5126 /* Look for named resource file in runtimepath */
5127 STRCPY(buffer, "print");
5128 add_pathsep(buffer);
5129 STRCAT(buffer, name);
5130 STRCAT(buffer, ".ps");
5131 resource_filename = resource->filename;
5132 *resource_filename = NUL;
5133 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
5134 && resource->filename[0] != NUL);
5135}
5136
5137/* PS CR and LF characters have platform independent values */
5138#define PSLF (0x0a)
5139#define PSCR (0x0d)
5140
Bram Moolenaar8299df92004-07-10 09:47:34 +00005141/* Static buffer to read initial comments in a resource file, some can have a
5142 * couple of KB of comments! */
5143#define PRT_FILE_BUFFER_LEN (2048)
5144struct prt_resfile_buffer_S
5145{
5146 char_u buffer[PRT_FILE_BUFFER_LEN];
5147 int len;
5148 int line_start;
5149 int line_end;
5150};
5151
5152static struct prt_resfile_buffer_S prt_resfile;
5153
5154 static int
5155prt_resfile_next_line()
5156{
5157 int index;
5158
5159 /* Move to start of next line and then find end of line */
5160 index = prt_resfile.line_end + 1;
5161 while (index < prt_resfile.len)
5162 {
5163 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
5164 != PSCR)
5165 break;
5166 index++;
5167 }
5168 prt_resfile.line_start = index;
5169
5170 while (index < prt_resfile.len)
5171 {
5172 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
5173 == PSCR)
5174 break;
5175 index++;
5176 }
5177 prt_resfile.line_end = index;
5178
5179 return (index < prt_resfile.len);
5180}
5181
5182 static int
5183prt_resfile_strncmp(offset, string, len)
5184 int offset;
5185 char *string;
5186 int len;
5187{
5188 /* Force not equal if string is longer than remainder of line */
5189 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
5190 return 1;
5191
5192 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
5193 string, len);
5194}
5195
5196 static int
5197prt_resfile_skip_nonws(offset)
5198 int offset;
5199{
5200 int index;
5201
5202 index = prt_resfile.line_start + offset;
5203 while (index < prt_resfile.line_end)
5204 {
5205 if (isspace(prt_resfile.buffer[index]))
5206 return index - prt_resfile.line_start;
5207 index++;
5208 }
5209 return -1;
5210}
5211
5212 static int
5213prt_resfile_skip_ws(offset)
5214 int offset;
5215{
5216 int index;
5217
5218 index = prt_resfile.line_start + offset;
5219 while (index < prt_resfile.line_end)
5220 {
5221 if (!isspace(prt_resfile.buffer[index]))
5222 return index - prt_resfile.line_start;
5223 index++;
5224 }
5225 return -1;
5226}
5227
5228/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
5229 * if a DSC comment is found, else false */
5230 static int
5231prt_next_dsc(p_dsc_line)
5232 struct prt_dsc_line_S *p_dsc_line;
5233{
5234 int comment;
5235 int offset;
5236
5237 /* Move to start of next line */
5238 if (!prt_resfile_next_line())
5239 return FALSE;
5240
5241 /* DSC comments always start %% */
5242 if (prt_resfile_strncmp(0, "%%", 2) != 0)
5243 return FALSE;
5244
5245 /* Find type of DSC comment */
5246 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
5247 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
5248 prt_dsc_table[comment].len) == 0)
5249 break;
5250
5251 if (comment != NUM_ELEMENTS(prt_dsc_table))
5252 {
5253 /* Return type of comment */
5254 p_dsc_line->type = prt_dsc_table[comment].type;
5255 offset = prt_dsc_table[comment].len;
5256 }
5257 else
5258 {
5259 /* Unrecognised DSC comment, skip to ws after comment leader */
5260 p_dsc_line->type = PRT_DSC_MISC_TYPE;
5261 offset = prt_resfile_skip_nonws(0);
5262 if (offset == -1)
5263 return FALSE;
5264 }
5265
5266 /* Skip ws to comment value */
5267 offset = prt_resfile_skip_ws(offset);
5268 if (offset == -1)
5269 return FALSE;
5270
5271 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
5272 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
5273
5274 return TRUE;
5275}
5276
5277/* Improved hand crafted parser to get the type, title, and version number of a
5278 * PS resource file so the file details can be added to the DSC header comments.
5279 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 static int
5281prt_open_resource(resource)
5282 struct prt_ps_resource_S *resource;
5283{
Bram Moolenaar8299df92004-07-10 09:47:34 +00005284 int offset;
5285 int seen_all;
5286 int seen_title;
5287 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005288 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005289 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290
5291 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5292 if (fd_resource == NULL)
5293 {
5294 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
5295 return FALSE;
5296 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005297 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298
5299 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005300 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
5301 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 if (ferror(fd_resource))
5303 {
5304 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5305 resource->filename);
5306 fclose(fd_resource);
5307 return FALSE;
5308 }
5309
Bram Moolenaar8299df92004-07-10 09:47:34 +00005310 prt_resfile.line_end = -1;
5311 prt_resfile.line_start = 0;
5312 if (!prt_resfile_next_line())
5313 return FALSE;
5314
5315 offset = 0;
5316
5317 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
5318 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 {
5320 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
5321 resource->filename);
5322 fclose(fd_resource);
5323 return FALSE;
5324 }
5325
5326 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005327 offset += STRLEN(PRT_RESOURCE_HEADER);
5328 offset = prt_resfile_skip_nonws(offset);
5329 if (offset == -1)
5330 return FALSE;
5331 offset = prt_resfile_skip_ws(offset);
5332 if (offset == -1)
5333 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005334
Bram Moolenaar8299df92004-07-10 09:47:34 +00005335 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
5336 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 {
5338 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5339 resource->filename);
5340 fclose(fd_resource);
5341 return FALSE;
5342 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005343 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005344
5345 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005346 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
5347 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005349 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
5350 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005352 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
5353 STRLEN(PRT_RESOURCE_CMAP)) == 0)
5354 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355 else
5356 {
5357 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5358 resource->filename);
5359 fclose(fd_resource);
5360 return FALSE;
5361 }
5362
Bram Moolenaar8299df92004-07-10 09:47:34 +00005363 /* Look for title and version of resource */
5364 resource->title[0] = '\0';
5365 resource->version[0] = '\0';
5366 seen_title = FALSE;
5367 seen_version = FALSE;
5368 seen_all = FALSE;
5369 while (!seen_all && prt_next_dsc(&dsc_line))
5370 {
5371 switch (dsc_line.type)
5372 {
5373 case PRT_DSC_TITLE_TYPE:
5374 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
5375 resource->title[dsc_line.len] = '\0';
5376 seen_title = TRUE;
5377 if (seen_version)
5378 seen_all = TRUE;
5379 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005380
Bram Moolenaar8299df92004-07-10 09:47:34 +00005381 case PRT_DSC_VERSION_TYPE:
5382 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
5383 resource->version[dsc_line.len] = '\0';
5384 seen_version = TRUE;
5385 if (seen_title)
5386 seen_all = TRUE;
5387 break;
5388
5389 case PRT_DSC_ENDCOMMENTS_TYPE:
5390 /* Wont find title or resource after this comment, stop searching */
5391 seen_all = TRUE;
5392 break;
5393
5394 case PRT_DSC_MISC_TYPE:
5395 /* Not interested in whatever comment this line had */
5396 break;
5397 }
5398 }
5399
5400 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005401 {
5402 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5403 resource->filename);
5404 fclose(fd_resource);
5405 return FALSE;
5406 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407
5408 fclose(fd_resource);
5409
5410 return TRUE;
5411}
5412
5413 static int
5414prt_check_resource(resource, version)
5415 struct prt_ps_resource_S *resource;
5416 char_u *version;
5417{
5418 /* Version number m.n should match, the revision number does not matter */
5419 if (STRNCMP(resource->version, version, STRLEN(version)))
5420 {
5421 EMSG2(_("E621: \"%s\" resource file has wrong version"),
5422 resource->name);
5423 return FALSE;
5424 }
5425
5426 /* Other checks to be added as needed */
5427 return TRUE;
5428}
5429
5430 static void
5431prt_dsc_start()
5432{
5433 prt_write_string("%!PS-Adobe-3.0\n");
5434}
5435
5436 static void
5437prt_dsc_noarg(comment)
5438 char *comment;
5439{
5440 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
5441 prt_write_file(prt_line_buffer);
5442}
5443
5444 static void
5445prt_dsc_textline(comment, text)
5446 char *comment;
5447 char *text;
5448{
5449 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
5450 prt_write_file(prt_line_buffer);
5451}
5452
5453 static void
5454prt_dsc_text(comment, text)
5455 char *comment;
5456 char *text;
5457{
5458 /* TODO - should scan 'text' for any chars needing escaping! */
5459 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
5460 prt_write_file(prt_line_buffer);
5461}
5462
5463#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
5464
5465 static void
5466prt_dsc_ints(comment, count, ints)
5467 char *comment;
5468 int count;
5469 int *ints;
5470{
5471 int i;
5472
5473 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
5474 prt_write_file(prt_line_buffer);
5475
5476 for (i = 0; i < count; i++)
5477 {
5478 sprintf((char *)prt_line_buffer, " %d", ints[i]);
5479 prt_write_file(prt_line_buffer);
5480 }
5481
5482 prt_write_string("\n");
5483}
5484
5485 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00005486prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487 char *comment; /* if NULL add to previous */
5488 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005489 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005490{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005491 if (comment != NULL)
5492 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
5493 else
5494 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
5495 prt_write_file(prt_line_buffer);
5496
Bram Moolenaar8299df92004-07-10 09:47:34 +00005497 sprintf((char *)prt_line_buffer, " %s\n", string);
5498 prt_write_file(prt_line_buffer);
5499}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005500
Bram Moolenaar8299df92004-07-10 09:47:34 +00005501 static void
5502prt_dsc_font_resource(resource, ps_font)
5503 char *resource;
5504 struct prt_ps_font_S *ps_font;
5505{
5506 int i;
5507
5508 prt_dsc_resources(resource, "font",
5509 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
5510 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
5511 if (ps_font->ps_fontname[i] != NULL)
5512 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513}
5514
5515 static void
5516prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
5517 int duplex;
5518 int tumble;
5519 int collate;
5520 int color;
5521 int num_copies;
5522{
5523 /* Only output the comment if we need to.
5524 * Note: tumble is ignored if we are not duplexing
5525 */
5526 if (!(duplex || collate || color || (num_copies > 1)))
5527 return;
5528
5529 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
5530 prt_write_file(prt_line_buffer);
5531
5532 if (duplex)
5533 {
5534 prt_write_string(" duplex");
5535 if (tumble)
5536 prt_write_string("(tumble)");
5537 }
5538 if (collate)
5539 prt_write_string(" collate");
5540 if (color)
5541 prt_write_string(" color");
5542 if (num_copies > 1)
5543 {
5544 prt_write_string(" numcopies(");
5545 /* Note: no space wanted so dont use prt_write_int() */
5546 sprintf((char *)prt_line_buffer, "%d", num_copies);
5547 prt_write_file(prt_line_buffer);
5548 prt_write_string(")");
5549 }
5550 prt_write_string("\n");
5551}
5552
5553 static void
5554prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
5555 char *paper_name;
5556 double width;
5557 double height;
5558 double weight;
5559 char *colour;
5560 char *type;
5561{
5562 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
5563 prt_write_file(prt_line_buffer);
5564 prt_write_real(width, 2);
5565 prt_write_real(height, 2);
5566 prt_write_real(weight, 2);
5567 if (colour == NULL)
5568 prt_write_string("()");
5569 else
5570 prt_write_string(colour);
5571 prt_write_string(" ");
5572 if (type == NULL)
5573 prt_write_string("()");
5574 else
5575 prt_write_string(type);
5576 prt_write_string("\n");
5577}
5578
5579 void
5580mch_print_cleanup()
5581{
5582#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005583 if (prt_out_mbyte)
5584 {
5585 int i;
5586
5587 /* Free off all CID font names created, but first clear duplicate
5588 * pointers to the same string (when the same font is used for more than
5589 * one style).
5590 */
5591 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5592 {
5593 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5594 vim_free(prt_ps_mb_font.ps_fontname[i]);
5595 prt_ps_mb_font.ps_fontname[i] = NULL;
5596 }
5597 }
5598
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 if (prt_do_conv)
5600 {
5601 convert_setup(&prt_conv, NULL, NULL);
5602 prt_do_conv = FALSE;
5603 }
5604#endif
5605 if (prt_ps_fd != NULL)
5606 {
5607 fclose(prt_ps_fd);
5608 prt_ps_fd = NULL;
5609 prt_file_error = FALSE;
5610 }
5611 if (prt_ps_file_name != NULL)
5612 {
5613 vim_free(prt_ps_file_name);
5614 prt_ps_file_name = NULL;
5615 }
5616}
5617
5618 static float
5619to_device_units(idx, physsize, def_number)
5620 int idx;
5621 double physsize;
5622 int def_number;
5623{
5624 float ret;
5625 int u;
5626 int nr;
5627
5628 u = prt_get_unit(idx);
5629 if (u == PRT_UNIT_NONE)
5630 {
5631 u = PRT_UNIT_PERC;
5632 nr = def_number;
5633 }
5634 else
5635 nr = printer_opts[idx].number;
5636
5637 switch (u)
5638 {
5639 case PRT_UNIT_INCH:
5640 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5641 break;
5642 case PRT_UNIT_MM:
5643 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5644 break;
5645 case PRT_UNIT_POINT:
5646 ret = (float)nr;
5647 break;
5648 case PRT_UNIT_PERC:
5649 default:
5650 ret = (float)(physsize * nr) / 100;
5651 break;
5652 }
5653
5654 return ret;
5655}
5656
5657/*
5658 * Calculate margins for given width and height from printoptions settings.
5659 */
5660 static void
5661prt_page_margins(width, height, left, right, top, bottom)
5662 double width;
5663 double height;
5664 double *left;
5665 double *right;
5666 double *top;
5667 double *bottom;
5668{
5669 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5670 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5671 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5672 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5673}
5674
5675 static void
5676prt_font_metrics(font_scale)
5677 int font_scale;
5678{
5679 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005680 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681}
5682
5683
5684 static int
5685prt_get_cpl()
5686{
5687 if (prt_use_number())
5688 {
5689 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005690#ifdef FEAT_MBYTE
5691 /* If we are outputting multi-byte characters then line numbers will be
5692 * printed with half width characters
5693 */
5694 if (prt_out_mbyte)
5695 prt_number_width /= 2;
5696#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005697 prt_left_margin += prt_number_width;
5698 }
5699 else
5700 prt_number_width = 0.0;
5701
5702 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5703}
5704
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005705#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005706 static int
5707prt_build_cid_fontname(font, name, name_len)
5708 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005709 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005710 int name_len;
5711{
5712 char *fontname;
5713
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005714 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005715 if (fontname == NULL)
5716 return FALSE;
5717 STRNCPY(fontname, name, name_len);
5718 fontname[name_len] = '\0';
5719 prt_ps_mb_font.ps_fontname[font] = fontname;
5720
5721 return TRUE;
5722}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005723#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005724
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725/*
5726 * Get number of lines of text that fit on a page (excluding the header).
5727 */
5728 static int
5729prt_get_lpp()
5730{
5731 int lpp;
5732
5733 /*
5734 * Calculate offset to lower left corner of background rect based on actual
5735 * font height (based on its bounding box) and the line height, handling the
5736 * case where the font height can exceed the line height.
5737 */
5738 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005739 prt_ps_font->bbox_min_y);
5740 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005741 {
5742 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005743 (1000.0 - (prt_ps_font->bbox_max_y -
5744 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745 }
5746
5747 /* Get height for topmost line based on background rect offset. */
5748 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5749
5750 /* Calculate lpp */
5751 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5752
5753 /* Adjust top margin if there is a header */
5754 prt_top_margin -= prt_line_height * prt_header_height();
5755
5756 return lpp - prt_header_height();
5757}
5758
Bram Moolenaar8299df92004-07-10 09:47:34 +00005759#ifdef FEAT_MBYTE
5760 static int
5761prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005762 char *p_encoding;
5763 struct prt_ps_mbfont_S *p_cmap;
5764 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005765{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005766 int mbenc;
5767 int enc_len;
5768 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005769
5770 *pp_mbenc = NULL;
5771 /* Look for recognised encoding */
5772 enc_len = STRLEN(p_encoding);
5773 p_mbenc = p_cmap->encodings;
5774 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5775 {
5776 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5777 {
5778 *pp_mbenc = p_mbenc;
5779 return TRUE;
5780 }
5781 p_mbenc++;
5782 }
5783 return FALSE;
5784}
5785
5786 static int
5787prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005788 char *p_charset;
5789 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005790 struct prt_ps_charset_S **pp_mbchar;
5791{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005792 int mbchar;
5793 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005794 struct prt_ps_charset_S *p_mbchar;
5795
5796 /* Look for recognised character set, using default if one is not given */
5797 if (*p_charset == NUL)
5798 p_charset = p_cmap->defcs;
5799 char_len = STRLEN(p_charset);
5800 p_mbchar = p_cmap->charsets;
5801 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5802 {
5803 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5804 {
5805 *pp_mbchar = p_mbchar;
5806 return TRUE;
5807 }
5808 p_mbchar++;
5809 }
5810 return FALSE;
5811}
5812#endif
5813
Bram Moolenaar071d4272004-06-13 20:20:40 +00005814/*ARGSUSED*/
5815 int
5816mch_print_init(psettings, jobname, forceit)
5817 prt_settings_T *psettings;
5818 char_u *jobname;
5819 int forceit;
5820{
5821 int i;
5822 char *paper_name;
5823 int paper_strlen;
5824 int fontsize;
5825 char_u *p;
5826 double left;
5827 double right;
5828 double top;
5829 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005830#ifdef FEAT_MBYTE
5831 int cmap;
5832 int pmcs_len;
5833 char_u *p_encoding;
5834 struct prt_ps_encoding_S *p_mbenc;
5835 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005836 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005837#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838
5839#if 0
5840 /*
5841 * TODO:
5842 * If "forceit" is false: pop up a dialog to select:
5843 * - printer name
5844 * - copies
5845 * - collated/uncollated
5846 * - duplex off/long side/short side
5847 * - paper size
5848 * - portrait/landscape
5849 * - font size
5850 *
5851 * If "forceit" is true: use the default printer and settings
5852 */
5853 if (forceit)
5854 s_pd.Flags |= PD_RETURNDEFAULT;
5855#endif
5856
5857 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005858 * Set up font and encoding.
5859 */
5860#ifdef FEAT_MBYTE
5861 p_encoding = enc_skip(p_penc);
5862 if (*p_encoding == NUL)
5863 p_encoding = enc_skip(p_enc);
5864
5865 /* Look for recognised multi-byte coding, and if the charset is recognised.
5866 * This is to cope with the fact that various unicode encodings are
5867 * supported in more than one of CJK. */
5868 p_mbenc = NULL;
5869 p_mbenc_first = NULL;
5870 p_mbchar = NULL;
5871 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005872 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5873 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005874 {
5875 if (p_mbenc_first == NULL)
5876 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005877 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5878 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005879 break;
5880 }
5881
5882 /* Use first encoding matched if no charset matched */
5883 if (p_mbchar == NULL && p_mbenc_first != NULL)
5884 p_mbenc = p_mbenc_first;
5885
5886 prt_out_mbyte = (p_mbenc != NULL);
5887 if (prt_out_mbyte)
5888 {
5889 /* Build CMap name - will be same for all multi-byte fonts used */
5890 prt_cmap[0] = '\0';
5891
5892 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5893
5894 if (!prt_custom_cmap)
5895 {
5896 /* Check encoding and character set are compatible */
5897 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5898 {
5899 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5900 return FALSE;
5901 }
5902
5903 /* Add charset name if not empty */
5904 if (p_mbchar->cmap_charset != NULL)
5905 {
5906 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5907 STRCAT(prt_cmap, "-");
5908 }
5909 }
5910 else
5911 {
5912 /* Add custom CMap character set name */
5913 pmcs_len = STRLEN(p_pmcs);
5914 if (pmcs_len == 0)
5915 {
5916 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5917 return FALSE;
5918 }
5919 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5920 prt_cmap[pmcs_len] = '\0';
5921 STRCAT(prt_cmap, "-");
5922 }
5923
5924 /* CMap name ends with (optional) encoding name and -H for horizontal */
5925 if (p_mbenc->cmap_encoding != NULL)
5926 {
5927 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5928 STRCAT(prt_cmap, "-");
5929 }
5930 STRCAT(prt_cmap, "H");
5931
5932 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5933 {
5934 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5935 return FALSE;
5936 }
5937
5938 /* Derive CID font names with fallbacks if not defined */
5939 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5940 mbfont_opts[OPT_MBFONT_REGULAR].string,
5941 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5942 return FALSE;
5943 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5944 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5945 mbfont_opts[OPT_MBFONT_BOLD].string,
5946 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5947 return FALSE;
5948 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5949 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5950 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
5951 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
5952 return FALSE;
5953 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
5954 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005955 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
5956 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005957 return FALSE;
5958
5959 /* Check if need to use Courier for ASCII code range, and if so pick up
5960 * the encoding to use */
5961 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
5962 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
5963 if (prt_use_courier)
5964 {
5965 /* Use national ASCII variant unless ASCII wanted */
5966 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
5967 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
5968 prt_ascii_encoding = "ascii";
5969 else
5970 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
5971 }
5972
5973 prt_ps_font = &prt_ps_mb_font;
5974 }
5975 else
5976#endif
5977 {
5978#ifdef FEAT_MBYTE
5979 prt_use_courier = FALSE;
5980#endif
5981 prt_ps_font = &prt_ps_courier_font;
5982 }
5983
5984 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985 * Find the size of the paper and set the margins.
5986 */
5987 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
5988 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
5989 if (printer_opts[OPT_PRINT_PAPER].present)
5990 {
5991 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
5992 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
5993 }
5994 else
5995 {
5996 paper_name = "A4";
5997 paper_strlen = 2;
5998 }
5999 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
6000 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
6001 && STRNICMP(prt_mediasize[i].name, paper_name,
6002 paper_strlen) == 0)
6003 break;
6004 if (i == PRT_MEDIASIZE_LEN)
6005 i = 0;
6006 prt_media = i;
6007
6008 /*
6009 * Set PS pagesize based on media dimensions and print orientation.
6010 * Note: Media and page sizes have defined meanings in PostScript and should
6011 * be kept distinct. Media is the paper (or transparency, or ...) that is
6012 * printed on, whereas the page size is the area that the PostScript
6013 * interpreter renders into.
6014 */
6015 if (prt_portrait)
6016 {
6017 prt_page_width = prt_mediasize[i].width;
6018 prt_page_height = prt_mediasize[i].height;
6019 }
6020 else
6021 {
6022 prt_page_width = prt_mediasize[i].height;
6023 prt_page_height = prt_mediasize[i].width;
6024 }
6025
6026 /*
6027 * Set PS page margins based on the PS pagesize, not the mediasize - this
6028 * needs to be done before the cpl and lpp are calculated.
6029 */
6030 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
6031 &bottom);
6032 prt_left_margin = (float)left;
6033 prt_right_margin = (float)right;
6034 prt_top_margin = (float)top;
6035 prt_bottom_margin = (float)bottom;
6036
6037 /*
6038 * Set up the font size.
6039 */
6040 fontsize = PRT_PS_DEFAULT_FONTSIZE;
6041 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
6042 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
6043 fontsize = atoi((char *)p + 2);
6044 prt_font_metrics(fontsize);
6045
6046 /*
6047 * Return the number of characters per line, and lines per page for the
6048 * generic print code.
6049 */
6050 psettings->chars_per_line = prt_get_cpl();
6051 psettings->lines_per_page = prt_get_lpp();
6052
6053 /* Catch margin settings that leave no space for output! */
6054 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
6055 return FAIL;
6056
6057 /*
6058 * Sort out the number of copies to be printed. PS by default will do
6059 * uncollated copies for you, so once we know how many uncollated copies are
6060 * wanted cache it away and lie to the generic code that we only want one
6061 * uncollated copy.
6062 */
6063 psettings->n_collated_copies = 1;
6064 psettings->n_uncollated_copies = 1;
6065 prt_num_copies = 1;
6066 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
6067 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
6068 if (prt_collate)
6069 {
6070 /* TODO: Get number of collated copies wanted. */
6071 psettings->n_collated_copies = 1;
6072 }
6073 else
6074 {
6075 /* TODO: Get number of uncollated copies wanted and update the cached
6076 * count.
6077 */
6078 prt_num_copies = 1;
6079 }
6080
6081 psettings->jobname = jobname;
6082
6083 /*
6084 * Set up printer duplex and tumble based on Duplex option setting - default
6085 * is long sided duplex printing (i.e. no tumble).
6086 */
6087 prt_duplex = TRUE;
6088 prt_tumble = FALSE;
6089 psettings->duplex = 1;
6090 if (printer_opts[OPT_PRINT_DUPLEX].present)
6091 {
6092 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
6093 {
6094 prt_duplex = FALSE;
6095 psettings->duplex = 0;
6096 }
6097 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
6098 == 0)
6099 prt_tumble = TRUE;
6100 }
6101
6102 /* For now user abort not supported */
6103 psettings->user_abort = 0;
6104
6105 /* If the user didn't specify a file name, use a temp file. */
6106 if (psettings->outfile == NULL)
6107 {
6108 prt_ps_file_name = vim_tempname('p');
6109 if (prt_ps_file_name == NULL)
6110 {
6111 EMSG(_(e_notmp));
6112 return FAIL;
6113 }
6114 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
6115 }
6116 else
6117 {
6118 p = expand_env_save(psettings->outfile);
6119 if (p != NULL)
6120 {
6121 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
6122 vim_free(p);
6123 }
6124 }
6125 if (prt_ps_fd == NULL)
6126 {
6127 EMSG(_("E324: Can't open PostScript output file"));
6128 mch_print_cleanup();
6129 return FAIL;
6130 }
6131
Bram Moolenaar8299df92004-07-10 09:47:34 +00006132 prt_bufsiz = psettings->chars_per_line;
6133#ifdef FEAT_MBYTE
6134 if (prt_out_mbyte)
6135 prt_bufsiz *= 2;
6136#endif
6137 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006138
6139 prt_page_num = 0;
6140
6141 prt_attribute_change = FALSE;
6142 prt_need_moveto = FALSE;
6143 prt_need_font = FALSE;
6144 prt_need_fgcol = FALSE;
6145 prt_need_bgcol = FALSE;
6146 prt_need_underline = FALSE;
6147
6148 prt_file_error = FALSE;
6149
6150 return OK;
6151}
6152
6153 static int
6154prt_add_resource(resource)
6155 struct prt_ps_resource_S *resource;
6156{
6157 FILE* fd_resource;
6158 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006159 size_t bytes_read;
6160
6161 fd_resource = mch_fopen((char *)resource->filename, READBIN);
6162 if (fd_resource == NULL)
6163 {
6164 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
6165 return FALSE;
6166 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006167 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
6168 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006169
6170 prt_dsc_textline("BeginDocument", (char *)resource->filename);
6171
6172 for (;;)
6173 {
6174 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
6175 sizeof(resource_buffer), fd_resource);
6176 if (ferror(fd_resource))
6177 {
6178 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
6179 resource->filename);
6180 fclose(fd_resource);
6181 return FALSE;
6182 }
6183 if (bytes_read == 0)
6184 break;
6185 prt_write_file_raw_len(resource_buffer, bytes_read);
6186 if (prt_file_error)
6187 {
6188 fclose(fd_resource);
6189 return FALSE;
6190 }
6191 }
6192 fclose(fd_resource);
6193
6194 prt_dsc_noarg("EndDocument");
6195
6196 prt_dsc_noarg("EndResource");
6197
6198 return TRUE;
6199}
6200
6201 int
6202mch_print_begin(psettings)
6203 prt_settings_T *psettings;
6204{
6205 time_t now;
6206 int bbox[4];
6207 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006208 double left;
6209 double right;
6210 double top;
6211 double bottom;
6212 struct prt_ps_resource_S res_prolog;
6213 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006214 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006215 char_u *p_encoding;
6216#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006217 struct prt_ps_resource_S res_cidfont;
6218 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006219#endif
6220
6221 /*
6222 * PS DSC Header comments - no PS code!
6223 */
6224 prt_dsc_start();
6225 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006226 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006227 STRCPY(buffer, "Unknown");
6228 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006229 prt_dsc_textline("Creator", VIM_VERSION_LONG);
6230 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
6231 now = time(NULL);
6232 p_time = ctime(&now);
6233 /* Note: ctime() adds a \n so we have to remove it :-( */
6234 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
6235 prt_dsc_textline("CreationDate", p_time);
6236 prt_dsc_textline("DocumentData", "Clean8Bit");
6237 prt_dsc_textline("Orientation", "Portrait");
6238 prt_dsc_atend("Pages");
6239 prt_dsc_textline("PageOrder", "Ascend");
6240 /* The bbox does not change with orientation - it is always in the default
6241 * user coordinate system! We have to recalculate right and bottom
6242 * coordinates based on the font metrics for the bbox to be accurate. */
6243 prt_page_margins(prt_mediasize[prt_media].width,
6244 prt_mediasize[prt_media].height,
6245 &left, &right, &top, &bottom);
6246 bbox[0] = (int)left;
6247 if (prt_portrait)
6248 {
6249 /* In portrait printing the fixed point is the top left corner so we
6250 * derive the bbox from that point. We have the expected cpl chars
6251 * across the media and lpp lines down the media.
6252 */
6253 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
6254 * prt_line_height);
6255 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
6256 + 0.5);
6257 bbox[3] = (int)(top + 0.5);
6258 }
6259 else
6260 {
6261 /* In landscape printing the fixed point is the bottom left corner so we
6262 * derive the bbox from that point. We have lpp chars across the media
6263 * and cpl lines up the media.
6264 */
6265 bbox[1] = (int)bottom;
6266 bbox[2] = (int)(left + ((psettings->lines_per_page
6267 + prt_header_height()) * prt_line_height) + 0.5);
6268 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
6269 + 0.5);
6270 }
6271 prt_dsc_ints("BoundingBox", 4, bbox);
6272 /* The media width and height does not change with landscape printing! */
6273 prt_dsc_docmedia(prt_mediasize[prt_media].name,
6274 prt_mediasize[prt_media].width,
6275 prt_mediasize[prt_media].height,
6276 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006277 /* Define fonts needed */
6278#ifdef FEAT_MBYTE
6279 if (!prt_out_mbyte || prt_use_courier)
6280#endif
6281 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
6282#ifdef FEAT_MBYTE
6283 if (prt_out_mbyte)
6284 {
6285 prt_dsc_font_resource((prt_use_courier ? NULL
6286 : "DocumentNeededResources"), &prt_ps_mb_font);
6287 if (!prt_custom_cmap)
6288 prt_dsc_resources(NULL, "cmap", prt_cmap);
6289 }
6290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
Bram Moolenaar8299df92004-07-10 09:47:34 +00006292 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293 if (!prt_find_resource("prolog", &res_prolog))
6294 {
6295 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
6296 return FALSE;
6297 }
6298 if (!prt_open_resource(&res_prolog))
6299 return FALSE;
6300 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
6301 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006302#ifdef FEAT_MBYTE
6303 if (prt_out_mbyte)
6304 {
6305 /* Look for required version of multi-byte printing procset */
6306 if (!prt_find_resource("cidfont", &res_cidfont))
6307 {
6308 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
6309 return FALSE;
6310 }
6311 if (!prt_open_resource(&res_cidfont))
6312 return FALSE;
6313 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
6314 return FALSE;
6315 }
6316#endif
6317
Bram Moolenaar071d4272004-06-13 20:20:40 +00006318 /* Find an encoding to use for printing.
6319 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
6320 * that cannot be found then default to "latin1".
6321 * Note: VIM specific encoding header is always skipped.
6322 */
6323#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006324 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006327 p_encoding = enc_skip(p_penc);
6328 if (*p_encoding == NUL
6329 || !prt_find_resource((char *)p_encoding, &res_encoding))
6330 {
6331 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006333 int props;
6334
6335 p_encoding = enc_skip(p_enc);
6336 props = enc_canon_props(p_encoding);
6337 if (!(props & ENC_8BIT)
6338 || !prt_find_resource((char *)p_encoding, &res_encoding))
6339 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006341 {
6342 /* Use latin1 as default printing encoding */
6343 p_encoding = (char_u *)"latin1";
6344 if (!prt_find_resource((char *)p_encoding, &res_encoding))
6345 {
6346 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
6347 p_encoding);
6348 return FALSE;
6349 }
6350 }
6351 }
6352 if (!prt_open_resource(&res_encoding))
6353 return FALSE;
6354 /* For the moment there are no checks on encoding resource files to
6355 * perform */
6356#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006358 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006360 p_encoding = enc_skip(p_penc);
6361 if (*p_encoding == NUL)
6362 p_encoding = enc_skip(p_enc);
6363 if (prt_use_courier)
6364 {
6365 /* Include ASCII range encoding vector */
6366 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
6367 {
6368 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006369 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006370 return FALSE;
6371 }
6372 if (!prt_open_resource(&res_encoding))
6373 return FALSE;
6374 /* For the moment there are no checks on encoding resource files to
6375 * perform */
6376 }
6377 }
6378
6379 prt_conv.vc_type = CONV_NONE;
6380 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
6381 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
6383 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006384 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006385 p_encoding);
6386 return FALSE;
6387 }
6388 prt_do_conv = TRUE;
6389 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006390 prt_do_conv = prt_conv.vc_type != CONV_NONE;
6391
6392 if (prt_out_mbyte && prt_custom_cmap)
6393 {
6394 /* Find user supplied CMap */
6395 if (!prt_find_resource(prt_cmap, &res_cmap))
6396 {
6397 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006398 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006399 return FALSE;
6400 }
6401 if (!prt_open_resource(&res_cmap))
6402 return FALSE;
6403 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006404#endif
6405
6406 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407 STRCPY(buffer, res_prolog.title);
6408 STRCAT(buffer, " ");
6409 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006410 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
6411#ifdef FEAT_MBYTE
6412 if (prt_out_mbyte)
6413 {
6414 STRCPY(buffer, res_cidfont.title);
6415 STRCAT(buffer, " ");
6416 STRCAT(buffer, res_cidfont.version);
6417 prt_dsc_resources(NULL, "procset", buffer);
6418
6419 if (prt_custom_cmap)
6420 {
6421 STRCPY(buffer, res_cmap.title);
6422 STRCAT(buffer, " ");
6423 STRCAT(buffer, res_cmap.version);
6424 prt_dsc_resources(NULL, "cmap", buffer);
6425 }
6426 }
6427 if (!prt_out_mbyte || prt_use_courier)
6428#endif
6429 {
6430 STRCPY(buffer, res_encoding.title);
6431 STRCAT(buffer, " ");
6432 STRCAT(buffer, res_encoding.version);
6433 prt_dsc_resources(NULL, "encoding", buffer);
6434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006435 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
6436#ifdef FEAT_SYN_HL
6437 psettings->do_syntax
6438#else
6439 0
6440#endif
6441 , prt_num_copies);
6442 prt_dsc_noarg("EndComments");
6443
6444 /*
6445 * PS Document page defaults
6446 */
6447 prt_dsc_noarg("BeginDefaults");
6448
6449 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006450#ifdef FEAT_MBYTE
6451 if (!prt_out_mbyte || prt_use_courier)
6452#endif
6453 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
6454#ifdef FEAT_MBYTE
6455 if (prt_out_mbyte)
6456 {
6457 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
6458 &prt_ps_mb_font);
6459 if (!prt_custom_cmap)
6460 prt_dsc_resources(NULL, "cmap", prt_cmap);
6461 }
6462#endif
6463
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464 /* Paper will be used for all pages */
6465 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
6466
6467 prt_dsc_noarg("EndDefaults");
6468
6469 /*
6470 * PS Document prolog inclusion - all required procsets.
6471 */
6472 prt_dsc_noarg("BeginProlog");
6473
Bram Moolenaar8299df92004-07-10 09:47:34 +00006474 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475 if (!prt_add_resource(&res_prolog))
6476 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006477#ifdef FEAT_MBYTE
6478 if (prt_out_mbyte)
6479 {
6480 /* Add CID font procset, and any user supplied CMap */
6481 if (!prt_add_resource(&res_cidfont))
6482 return FALSE;
6483 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
6484 return FALSE;
6485 }
6486#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487
Bram Moolenaar8299df92004-07-10 09:47:34 +00006488#ifdef FEAT_MBYTE
6489 if (!prt_out_mbyte || prt_use_courier)
6490#endif
6491 /* There will be only one Roman font encoding to be included in the PS
6492 * file. */
6493 if (!prt_add_resource(&res_encoding))
6494 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006495
6496 prt_dsc_noarg("EndProlog");
6497
6498 /*
6499 * PS Document setup - must appear after the prolog
6500 */
6501 prt_dsc_noarg("BeginSetup");
6502
6503 /* Device setup - page size and number of uncollated copies */
6504 prt_write_int((int)prt_mediasize[prt_media].width);
6505 prt_write_int((int)prt_mediasize[prt_media].height);
6506 prt_write_int(0);
6507 prt_write_string("sps\n");
6508 prt_write_int(prt_num_copies);
6509 prt_write_string("nc\n");
6510 prt_write_boolean(prt_duplex);
6511 prt_write_boolean(prt_tumble);
6512 prt_write_string("dt\n");
6513 prt_write_boolean(prt_collate);
6514 prt_write_string("c\n");
6515
6516 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006517#ifdef FEAT_MBYTE
6518 if (!prt_out_mbyte || prt_use_courier)
6519 {
6520 /* When using Courier for ASCII range when printing multi-byte, need to
6521 * pick up ASCII encoding to use with it. */
6522 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006523 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006524#endif
6525 prt_dsc_resources("IncludeResource", "font",
6526 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6527 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
6528 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6529 prt_dsc_resources("IncludeResource", "font",
6530 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6531 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
6532 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6533 prt_dsc_resources("IncludeResource", "font",
6534 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6535 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
6536 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6537 prt_dsc_resources("IncludeResource", "font",
6538 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6539 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
6540 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6541#ifdef FEAT_MBYTE
6542 }
6543 if (prt_out_mbyte)
6544 {
6545 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
6546 * not have an italic form being a western style, so where no font is
6547 * defined for these faces VIM falls back to an existing face.
6548 * Note: if using Courier for the ASCII range then the printout will
6549 * have bold/italic/bolditalic regardless of the setting of printmbfont.
6550 */
6551 prt_dsc_resources("IncludeResource", "font",
6552 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6553 if (!prt_custom_cmap)
6554 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6555 prt_def_cidfont("CF0", (int)prt_line_height,
6556 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6557
6558 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
6559 {
6560 prt_dsc_resources("IncludeResource", "font",
6561 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6562 if (!prt_custom_cmap)
6563 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6564 prt_def_cidfont("CF1", (int)prt_line_height,
6565 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6566 }
6567 else
6568 /* Use ROMAN for BOLD */
6569 prt_dup_cidfont("CF0", "CF1");
6570
6571 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
6572 {
6573 prt_dsc_resources("IncludeResource", "font",
6574 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6575 if (!prt_custom_cmap)
6576 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6577 prt_def_cidfont("CF2", (int)prt_line_height,
6578 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6579 }
6580 else
6581 /* Use ROMAN for OBLIQUE */
6582 prt_dup_cidfont("CF0", "CF2");
6583
6584 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
6585 {
6586 prt_dsc_resources("IncludeResource", "font",
6587 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6588 if (!prt_custom_cmap)
6589 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6590 prt_def_cidfont("CF3", (int)prt_line_height,
6591 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6592 }
6593 else
6594 /* Use BOLD for BOLDOBLIQUE */
6595 prt_dup_cidfont("CF1", "CF3");
6596 }
6597#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598
6599 /* Misc constant vars used for underlining and background rects */
6600 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006601 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006602 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006603 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006604 prt_def_var("BO", prt_bgcol_offset, 2);
6605
6606 prt_dsc_noarg("EndSetup");
6607
6608 /* Fail if any problems writing out to the PS file */
6609 return !prt_file_error;
6610}
6611
6612 void
6613mch_print_end(psettings)
6614 prt_settings_T *psettings;
6615{
6616 prt_dsc_noarg("Trailer");
6617
6618 /*
6619 * Output any info we don't know in toto until we finish
6620 */
6621 prt_dsc_ints("Pages", 1, &prt_page_num);
6622
6623 prt_dsc_noarg("EOF");
6624
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006625 /* Write CTRL-D to close serial communication link if used.
6626 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006627 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006628
Bram Moolenaar071d4272004-06-13 20:20:40 +00006629 if (!prt_file_error && psettings->outfile == NULL
6630 && !got_int && !psettings->user_abort)
6631 {
6632 /* Close the file first. */
6633 if (prt_ps_fd != NULL)
6634 {
6635 fclose(prt_ps_fd);
6636 prt_ps_fd = NULL;
6637 }
6638 prt_message((char_u *)_("Sending to printer..."));
6639
6640 /* Not printing to a file: use 'printexpr' to print the file. */
6641 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6642 EMSG(_("E365: Failed to print PostScript file"));
6643 else
6644 prt_message((char_u *)_("Print job sent."));
6645 }
6646
6647 mch_print_cleanup();
6648}
6649
6650 int
6651mch_print_end_page()
6652{
6653 prt_flush_buffer();
6654
6655 prt_write_string("re sp\n");
6656
6657 prt_dsc_noarg("PageTrailer");
6658
6659 return !prt_file_error;
6660}
6661
6662/*ARGSUSED*/
6663 int
6664mch_print_begin_page(str)
6665 char_u *str;
6666{
6667 int page_num[2];
6668
6669 prt_page_num++;
6670
6671 page_num[0] = page_num[1] = prt_page_num;
6672 prt_dsc_ints("Page", 2, page_num);
6673
6674 prt_dsc_noarg("BeginPageSetup");
6675
Bram Moolenaar8299df92004-07-10 09:47:34 +00006676 prt_write_string("sv\n0 g\n");
6677#ifdef FEAT_MBYTE
6678 prt_in_ascii = !prt_out_mbyte;
6679 if (prt_out_mbyte)
6680 prt_write_string("CF0 sf\n");
6681 else
6682#endif
6683 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006684 prt_fgcol = PRCOLOR_BLACK;
6685 prt_bgcol = PRCOLOR_WHITE;
6686 prt_font = PRT_PS_FONT_ROMAN;
6687
6688 /* Set up page transformation for landscape printing. */
6689 if (!prt_portrait)
6690 {
6691 prt_write_int(-((int)prt_mediasize[prt_media].width));
6692 prt_write_string("sl\n");
6693 }
6694
6695 prt_dsc_noarg("EndPageSetup");
6696
6697 /* We have reset the font attributes, force setting them again. */
6698 curr_bg = (long_u)0xffffffff;
6699 curr_fg = (long_u)0xffffffff;
6700 curr_bold = MAYBE;
6701
6702 return !prt_file_error;
6703}
6704
6705 int
6706mch_print_blank_page()
6707{
6708 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6709}
6710
6711static float prt_pos_x = 0;
6712static float prt_pos_y = 0;
6713
6714 void
6715mch_print_start_line(margin, page_line)
6716 int margin;
6717 int page_line;
6718{
6719 prt_pos_x = prt_left_margin;
6720 if (margin)
6721 prt_pos_x -= prt_number_width;
6722
6723 prt_pos_y = prt_top_margin - prt_first_line_height -
6724 page_line * prt_line_height;
6725
6726 prt_attribute_change = TRUE;
6727 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006728#ifdef FEAT_MBYTE
6729 prt_half_width = FALSE;
6730#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006731}
6732
6733/*ARGSUSED*/
6734 int
6735mch_print_text_out(p, len)
6736 char_u *p;
6737 int len;
6738{
6739 int need_break;
6740 char_u ch;
6741 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006742 float char_width;
6743 float next_pos;
6744#ifdef FEAT_MBYTE
6745 int in_ascii;
6746 int half_width;
6747#endif
6748
6749 char_width = prt_char_width;
6750
6751#ifdef FEAT_MBYTE
6752 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6753 * CJKV font to do what VIM is doing here - use a Roman font for characters
6754 * in the ASCII range, and the origingal CID font for everything else.
6755 * The problem is that GhostScript still (as of 8.13) does not support
6756 * rearranged fonts even though they have been documented by Adobe for 7
6757 * years! If they ever do, a lot of this code will disappear.
6758 */
6759 if (prt_use_courier)
6760 {
6761 in_ascii = (len == 1 && *p < 0x80);
6762 if (prt_in_ascii)
6763 {
6764 if (!in_ascii)
6765 {
6766 /* No longer in ASCII range - need to switch font */
6767 prt_in_ascii = FALSE;
6768 prt_need_font = TRUE;
6769 prt_attribute_change = TRUE;
6770 }
6771 }
6772 else if (in_ascii)
6773 {
6774 /* Now in ASCII range - need to switch font */
6775 prt_in_ascii = TRUE;
6776 prt_need_font = TRUE;
6777 prt_attribute_change = TRUE;
6778 }
6779 }
6780 if (prt_out_mbyte)
6781 {
6782 half_width = ((*mb_ptr2cells)(p) == 1);
6783 if (half_width)
6784 char_width /= 2;
6785 if (prt_half_width)
6786 {
6787 if (!half_width)
6788 {
6789 prt_half_width = FALSE;
6790 prt_pos_x += prt_char_width/4;
6791 prt_need_moveto = TRUE;
6792 prt_attribute_change = TRUE;
6793 }
6794 }
6795 else if (half_width)
6796 {
6797 prt_half_width = TRUE;
6798 prt_pos_x += prt_char_width/4;
6799 prt_need_moveto = TRUE;
6800 prt_attribute_change = TRUE;
6801 }
6802 }
6803#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006804
6805 /* Output any required changes to the graphics state, after flushing any
6806 * text buffered so far.
6807 */
6808 if (prt_attribute_change)
6809 {
6810 prt_flush_buffer();
6811 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006812 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006813
6814 if (prt_need_moveto)
6815 {
6816 prt_pos_x_moveto = prt_pos_x;
6817 prt_pos_y_moveto = prt_pos_y;
6818 prt_do_moveto = TRUE;
6819
6820 prt_need_moveto = FALSE;
6821 }
6822 if (prt_need_font)
6823 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006824#ifdef FEAT_MBYTE
6825 if (!prt_in_ascii)
6826 prt_write_string("CF");
6827 else
6828#endif
6829 prt_write_string("F");
6830 prt_write_int(prt_font);
6831 prt_write_string("sf\n");
6832 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006833 }
6834 if (prt_need_fgcol)
6835 {
6836 int r, g, b;
6837 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6838 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6839 b = prt_fgcol & 0xff;
6840
6841 prt_write_real(r / 255.0, 3);
6842 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006843 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844 else
6845 {
6846 prt_write_real(g / 255.0, 3);
6847 prt_write_real(b / 255.0, 3);
6848 prt_write_string("r\n");
6849 }
6850 prt_need_fgcol = FALSE;
6851 }
6852
6853 if (prt_bgcol != PRCOLOR_WHITE)
6854 {
6855 prt_new_bgcol = prt_bgcol;
6856 if (prt_need_bgcol)
6857 prt_do_bgcol = TRUE;
6858 }
6859 else
6860 prt_do_bgcol = FALSE;
6861 prt_need_bgcol = FALSE;
6862
6863 if (prt_need_underline)
6864 prt_do_underline = prt_underline;
6865 prt_need_underline = FALSE;
6866
6867 prt_attribute_change = FALSE;
6868 }
6869
6870#ifdef FEAT_MBYTE
6871 if (prt_do_conv)
6872 {
6873 /* Convert from multi-byte to 8-bit encoding */
6874 p = string_convert(&prt_conv, p, &len);
6875 if (p == NULL)
6876 p = (char_u *)"";
6877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878
Bram Moolenaar8299df92004-07-10 09:47:34 +00006879 if (prt_out_mbyte)
6880 {
6881 /* Multi-byte character strings are represented more efficiently as hex
6882 * strings when outputting clean 8 bit PS.
6883 */
6884 do
6885 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006886 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006887 ga_append(&prt_ps_buffer, ch);
6888 ch = prt_hexchar[(*p) & 0xf];
6889 ga_append(&prt_ps_buffer, ch);
6890 p++;
6891 }
6892 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006893 }
6894 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006895#endif
6896 {
6897 /* Add next character to buffer of characters to output.
6898 * Note: One printed character may require several PS characters to
6899 * represent it, but we only count them as one printed character.
6900 */
6901 ch = *p;
6902 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6903 {
6904 /* Convert non-printing characters to either their escape or octal
6905 * sequence, ensures PS sent over a serial line does not interfere
6906 * with the comms protocol. Note: For EBCDIC we need to write out
6907 * the escape sequences as ASCII codes!
6908 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6909 */
6910 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6911 switch (ch)
6912 {
6913 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6914 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6915 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6916 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6917 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6918 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6919 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6920 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6921
6922 default:
6923 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6924#ifdef EBCDIC
6925 ebcdic2ascii(ch_buff, 3);
6926#endif
6927 ga_append(&prt_ps_buffer, ch_buff[0]);
6928 ga_append(&prt_ps_buffer, ch_buff[1]);
6929 ga_append(&prt_ps_buffer, ch_buff[2]);
6930 break;
6931 }
6932 }
6933 else
6934 ga_append(&prt_ps_buffer, ch);
6935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006936
6937#ifdef FEAT_MBYTE
6938 /* Need to free any translated characters */
6939 if (prt_do_conv && (*p != NUL))
6940 vim_free(p);
6941#endif
6942
Bram Moolenaar8299df92004-07-10 09:47:34 +00006943 prt_text_run += char_width;
6944 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006945
Bram Moolenaar8299df92004-07-10 09:47:34 +00006946 /* The downside of fp - use relative error on right margin check */
6947 next_pos = prt_pos_x + prt_char_width;
6948 need_break = (next_pos > prt_right_margin) &&
6949 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006950
6951 if (need_break)
6952 prt_flush_buffer();
6953
6954 return need_break;
6955}
6956
6957 void
6958mch_print_set_font(iBold, iItalic, iUnderline)
6959 int iBold;
6960 int iItalic;
6961 int iUnderline;
6962{
6963 int font = 0;
6964
6965 if (iBold)
6966 font |= 0x01;
6967 if (iItalic)
6968 font |= 0x02;
6969
6970 if (font != prt_font)
6971 {
6972 prt_font = font;
6973 prt_attribute_change = TRUE;
6974 prt_need_font = TRUE;
6975 }
6976 if (prt_underline != iUnderline)
6977 {
6978 prt_underline = iUnderline;
6979 prt_attribute_change = TRUE;
6980 prt_need_underline = TRUE;
6981 }
6982}
6983
6984 void
6985mch_print_set_bg(bgcol)
6986 long_u bgcol;
6987{
6988 prt_bgcol = bgcol;
6989 prt_attribute_change = TRUE;
6990 prt_need_bgcol = TRUE;
6991}
6992
6993 void
6994mch_print_set_fg(fgcol)
6995 long_u fgcol;
6996{
6997 if (fgcol != (long_u)prt_fgcol)
6998 {
6999 prt_fgcol = fgcol;
7000 prt_attribute_change = TRUE;
7001 prt_need_fgcol = TRUE;
7002 }
7003}
7004
7005# endif /*FEAT_POSTSCRIPT*/
7006#endif /*FEAT_PRINTER*/
7007
7008#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7009 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
7010static char *get_locale_val __ARGS((int what));
7011
7012 static char *
7013get_locale_val(what)
7014 int what;
7015{
7016 char *loc;
7017
7018 /* Obtain the locale value from the libraries. For DJGPP this is
7019 * redefined and it doesn't use the arguments. */
7020 loc = setlocale(what, NULL);
7021
7022# if defined(__BORLANDC__)
7023 if (loc != NULL)
7024 {
7025 char_u *p;
7026
7027 /* Borland returns something like "LC_CTYPE=<name>\n"
7028 * Let's try to fix that bug here... */
7029 p = vim_strchr(loc, '=');
7030 if (p != NULL)
7031 {
7032 loc = ++p;
7033 while (*p != NUL) /* remove trailing newline */
7034 {
7035 if (*p < ' ')
7036 {
7037 *p = NUL;
7038 break;
7039 }
7040 ++p;
7041 }
7042 }
7043 }
7044# endif
7045
7046 return loc;
7047}
7048#endif
7049
7050
7051#ifdef WIN32
7052/*
7053 * On MS-Windows locale names are strings like "German_Germany.1252", but
7054 * gettext expects "de". Try to translate one into another here for a few
7055 * supported languages.
7056 */
7057 static char_u *
7058gettext_lang(char_u *name)
7059{
7060 int i;
7061 static char *(mtable[]) = {
7062 "afrikaans", "af",
7063 "czech", "cs",
7064 "dutch", "nl",
7065 "german", "de",
7066 "english_united kingdom", "en_GB",
7067 "spanish", "es",
7068 "french", "fr",
7069 "italian", "it",
7070 "japanese", "ja",
7071 "korean", "ko",
7072 "norwegian", "no",
7073 "polish", "pl",
7074 "russian", "ru",
7075 "slovak", "sk",
7076 "swedish", "sv",
7077 "ukrainian", "uk",
7078 "chinese_china", "zh_CN",
7079 "chinese_taiwan", "zh_TW",
7080 NULL};
7081
7082 for (i = 0; mtable[i] != NULL; i += 2)
7083 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
7084 return mtable[i + 1];
7085 return name;
7086}
7087#endif
7088
7089#if defined(FEAT_MULTI_LANG) || defined(PROTO)
7090/*
7091 * Obtain the current messages language. Used to set the default for
7092 * 'helplang'. May return NULL or an empty string.
7093 */
7094 char_u *
7095get_mess_lang()
7096{
7097 char_u *p;
7098
7099# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
7100# if defined(LC_MESSAGES)
7101 p = (char_u *)get_locale_val(LC_MESSAGES);
7102# else
7103 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
7104 * may be set to the LCID number. */
7105 p = (char_u *)get_locale_val(LC_ALL);
7106# endif
7107# else
7108 p = mch_getenv((char_u *)"LC_ALL");
7109 if (p == NULL || *p == NUL)
7110 {
7111 p = mch_getenv((char_u *)"LC_MESSAGES");
7112 if (p == NULL || *p == NUL)
7113 p = mch_getenv((char_u *)"LANG");
7114 }
7115# endif
7116# ifdef WIN32
7117 p = gettext_lang(p);
7118# endif
7119 return p;
7120}
7121#endif
7122
Bram Moolenaardef9e822004-12-31 20:58:58 +00007123/* Complicated #if; matches with where get_mess_env() is used below. */
7124#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7125 && defined(LC_MESSAGES))) \
7126 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7127 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
7128 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007129static char_u *get_mess_env __ARGS((void));
7130
7131/*
7132 * Get the language used for messages from the environment.
7133 */
7134 static char_u *
7135get_mess_env()
7136{
7137 char_u *p;
7138
7139 p = mch_getenv((char_u *)"LC_ALL");
7140 if (p == NULL || *p == NUL)
7141 {
7142 p = mch_getenv((char_u *)"LC_MESSAGES");
7143 if (p == NULL || *p == NUL)
7144 {
7145 p = mch_getenv((char_u *)"LANG");
7146 if (p != NULL && VIM_ISDIGIT(*p))
7147 p = NULL; /* ignore something like "1043" */
7148# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7149 if (p == NULL || *p == NUL)
7150 p = (char_u *)get_locale_val(LC_CTYPE);
7151# endif
7152 }
7153 }
7154 return p;
7155}
7156#endif
7157
7158#if defined(FEAT_EVAL) || defined(PROTO)
7159
7160/*
7161 * Set the "v:lang" variable according to the current locale setting.
7162 * Also do "v:lc_time"and "v:ctype".
7163 */
7164 void
7165set_lang_var()
7166{
7167 char_u *loc;
7168
7169# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7170 loc = (char_u *)get_locale_val(LC_CTYPE);
7171# else
7172 /* setlocale() not supported: use the default value */
7173 loc = (char_u *)"C";
7174# endif
7175 set_vim_var_string(VV_CTYPE, loc, -1);
7176
7177 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
7178 * back to LC_CTYPE if it's empty. */
7179# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
7180 loc = (char_u *)get_locale_val(LC_MESSAGES);
7181# else
7182 loc = get_mess_env();
7183# endif
7184 set_vim_var_string(VV_LANG, loc, -1);
7185
7186# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7187 loc = (char_u *)get_locale_val(LC_TIME);
7188# endif
7189 set_vim_var_string(VV_LC_TIME, loc, -1);
7190}
7191#endif
7192
7193#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7194 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
7195/*
7196 * ":language": Set the language (locale).
7197 */
7198 void
7199ex_language(eap)
7200 exarg_T *eap;
7201{
7202 char *loc;
7203 char_u *p;
7204 char_u *name;
7205 int what = LC_ALL;
7206 char *whatstr = "";
7207#ifdef LC_MESSAGES
7208# define VIM_LC_MESSAGES LC_MESSAGES
7209#else
7210# define VIM_LC_MESSAGES 6789
7211#endif
7212
7213 name = eap->arg;
7214
7215 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
7216 * Allow abbreviation, but require at least 3 characters to avoid
7217 * confusion with a two letter language name "me" or "ct". */
7218 p = skiptowhite(eap->arg);
7219 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
7220 {
7221 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
7222 {
7223 what = VIM_LC_MESSAGES;
7224 name = skipwhite(p);
7225 whatstr = "messages ";
7226 }
7227 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
7228 {
7229 what = LC_CTYPE;
7230 name = skipwhite(p);
7231 whatstr = "ctype ";
7232 }
7233 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
7234 {
7235 what = LC_TIME;
7236 name = skipwhite(p);
7237 whatstr = "time ";
7238 }
7239 }
7240
7241 if (*name == NUL)
7242 {
7243#ifndef LC_MESSAGES
7244 if (what == VIM_LC_MESSAGES)
7245 p = get_mess_env();
7246 else
7247#endif
7248 p = (char_u *)setlocale(what, NULL);
7249 if (p == NULL || *p == NUL)
7250 p = (char_u *)"Unknown";
7251 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
7252 }
7253 else
7254 {
7255#ifndef LC_MESSAGES
7256 if (what == VIM_LC_MESSAGES)
7257 loc = "";
7258 else
7259#endif
7260 loc = setlocale(what, (char *)name);
7261 if (loc == NULL)
7262 EMSG2(_("E197: Cannot set language to \"%s\""), name);
7263 else
7264 {
7265#ifdef HAVE_NL_MSG_CAT_CNTR
7266 /* Need to do this for GNU gettext, otherwise cached translations
7267 * will be used again. */
7268 extern int _nl_msg_cat_cntr;
7269
7270 ++_nl_msg_cat_cntr;
7271#endif
7272 /* Reset $LC_ALL, otherwise it would overrule everyting. */
7273 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
7274
7275 if (what != LC_TIME)
7276 {
7277 /* Tell gettext() what to translate to. It apparently doesn't
7278 * use the currently effective locale. Also do this when
7279 * FEAT_GETTEXT isn't defined, so that shell commands use this
7280 * value. */
7281 if (what == LC_ALL)
7282 vim_setenv((char_u *)"LANG", name);
7283 if (what != LC_CTYPE)
7284 {
7285 char_u *mname;
7286#ifdef WIN32
7287 mname = gettext_lang(name);
7288#else
7289 mname = name;
7290#endif
7291 vim_setenv((char_u *)"LC_MESSAGES", mname);
7292#ifdef FEAT_MULTI_LANG
7293 set_helplang_default(mname);
7294#endif
7295 }
7296
7297 /* Set $LC_CTYPE, because it overrules $LANG, and
7298 * gtk_set_locale() calls setlocale() again. gnome_init()
7299 * sets $LC_CTYPE to "en_US" (that's a bug!). */
7300 if (what != VIM_LC_MESSAGES)
7301 vim_setenv((char_u *)"LC_CTYPE", name);
7302# ifdef FEAT_GUI_GTK
7303 /* Let GTK know what locale we're using. Not sure this is
7304 * really needed... */
7305 if (gui.in_use)
7306 (void)gtk_set_locale();
7307# endif
7308 }
7309
7310# ifdef FEAT_EVAL
7311 /* Set v:lang, v:lc_time and v:ctype to the final result. */
7312 set_lang_var();
7313# endif
7314 }
7315 }
7316}
7317
7318# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
7319/*
7320 * Function given to ExpandGeneric() to obtain the possible arguments of the
7321 * ":language" command.
7322 */
7323/*ARGSUSED*/
7324 char_u *
7325get_lang_arg(xp, idx)
7326 expand_T *xp;
7327 int idx;
7328{
7329 if (idx == 0)
7330 return (char_u *)"messages";
7331 if (idx == 1)
7332 return (char_u *)"ctype";
7333 if (idx == 2)
7334 return (char_u *)"time";
7335 return NULL;
7336}
7337# endif
7338
7339#endif