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