blob: b86e4812eab41974a8d97e915d881b57a22536dc [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
Bram Moolenaar82cf9b62005-06-07 21:09:25 +00001306 dialog_msg(buff, _("Save changes to \"%s\"?"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 (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)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003023
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024/*
3025 * ":scriptnames"
3026 */
3027/*ARGSUSED*/
3028 void
3029ex_scriptnames(eap)
3030 exarg_T *eap;
3031{
3032 int i;
3033
Bram Moolenaar05159a02005-02-26 23:04:13 +00003034 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
3035 if (SCRIPT_ITEM(i).sn_name != NULL)
3036 smsg((char_u *)"%3d: %s", i, SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037}
3038
3039# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
3040/*
3041 * Fix slashes in the list of script names for 'shellslash'.
3042 */
3043 void
3044scriptnames_slash_adjust()
3045{
3046 int i;
3047
Bram Moolenaar05159a02005-02-26 23:04:13 +00003048 for (i = 1; i <= script_items.ga_len; ++i)
3049 if (SCRIPT_ITEM(i).sn_name != NULL)
3050 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051}
3052# endif
3053
3054/*
3055 * Get a pointer to a script name. Used for ":verbose set".
3056 */
3057 char_u *
3058get_scriptname(id)
3059 scid_T id;
3060{
3061 if (id == SID_MODELINE)
3062 return (char_u *)"modeline";
3063 if (id == SID_CMDARG)
3064 return (char_u *)"--cmd argument";
3065 if (id == SID_CARG)
3066 return (char_u *)"-c argument";
3067 if (id == SID_ENV)
3068 return (char_u *)"environment variable";
Bram Moolenaar05159a02005-02-26 23:04:13 +00003069 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003070}
Bram Moolenaar05159a02005-02-26 23:04:13 +00003071
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003072# if defined(EXITFREE) || defined(PROTO)
3073 void
3074free_scriptnames()
3075{
3076 int i;
3077
3078 for (i = script_items.ga_len; i > 0; --i)
3079 vim_free(SCRIPT_ITEM(i).sn_name);
3080 ga_clear(&script_items);
3081}
3082# endif
3083
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084#endif
3085
3086#if defined(USE_CR) || defined(PROTO)
3087
3088# if defined(__MSL__) && (__MSL__ >= 22)
3089/*
3090 * Newer version of the Metrowerks library handle DOS and UNIX files
3091 * without help.
3092 * Test with earlier versions, MSL 2.2 is the library supplied with
3093 * Codewarrior Pro 2.
3094 */
3095 char *
3096fgets_cr(s, n, stream)
3097 char *s;
3098 int n;
3099 FILE *stream;
3100{
3101 return fgets(s, n, stream);
3102}
3103# else
3104/*
3105 * Version of fgets() which also works for lines ending in a <CR> only
3106 * (Macintosh format).
3107 * For older versions of the Metrowerks library.
3108 * At least CodeWarrior 9 needed this code.
3109 */
3110 char *
3111fgets_cr(s, n, stream)
3112 char *s;
3113 int n;
3114 FILE *stream;
3115{
3116 int c = 0;
3117 int char_read = 0;
3118
3119 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
3120 {
3121 c = fgetc(stream);
3122 s[char_read++] = c;
3123 /* If the file is in DOS format, we need to skip a NL after a CR. I
3124 * thought it was the other way around, but this appears to work... */
3125 if (c == '\n')
3126 {
3127 c = fgetc(stream);
3128 if (c != '\r')
3129 ungetc(c, stream);
3130 }
3131 }
3132
3133 s[char_read] = 0;
3134 if (char_read == 0)
3135 return NULL;
3136
3137 if (feof(stream) && char_read == 1)
3138 return NULL;
3139
3140 return s;
3141}
3142# endif
3143#endif
3144
3145/*
3146 * Get one full line from a sourced file.
3147 * Called by do_cmdline() when it's called from do_source().
3148 *
3149 * Return a pointer to the line in allocated memory.
3150 * Return NULL for end-of-file or some error.
3151 */
3152/* ARGSUSED */
3153 char_u *
3154getsourceline(c, cookie, indent)
3155 int c; /* not used */
3156 void *cookie;
3157 int indent; /* not used */
3158{
3159 struct source_cookie *sp = (struct source_cookie *)cookie;
3160 char_u *line;
3161 char_u *p, *s;
3162
3163#ifdef FEAT_EVAL
3164 /* If breakpoints have been added/deleted need to check for it. */
3165 if (sp->dbg_tick < debug_tick)
3166 {
3167 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3168 sp->dbg_tick = debug_tick;
3169 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00003170# ifdef FEAT_PROFILE
3171 if (do_profiling)
3172 script_line_end();
3173# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174#endif
3175 /*
3176 * Get current line. If there is a read-ahead line, use it, otherwise get
3177 * one now.
3178 */
3179 if (sp->finished)
3180 line = NULL;
3181 else if (sp->nextline == NULL)
3182 line = get_one_sourceline(sp);
3183 else
3184 {
3185 line = sp->nextline;
3186 sp->nextline = NULL;
3187 ++sourcing_lnum;
3188 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003189#ifdef FEAT_PROFILE
3190 if (line != NULL && do_profiling)
3191 script_line_start();
3192#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193
3194 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
3195 * contain the 'C' flag. */
3196 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
3197 {
3198 /* compensate for the one line read-ahead */
3199 --sourcing_lnum;
3200 for (;;)
3201 {
3202 sp->nextline = get_one_sourceline(sp);
3203 if (sp->nextline == NULL)
3204 break;
3205 p = skipwhite(sp->nextline);
3206 if (*p != '\\')
3207 break;
3208 s = alloc((int)(STRLEN(line) + STRLEN(p)));
3209 if (s == NULL) /* out of memory */
3210 break;
3211 STRCPY(s, line);
3212 STRCAT(s, p + 1);
3213 vim_free(line);
3214 line = s;
3215 vim_free(sp->nextline);
3216 }
3217 }
3218
3219#ifdef FEAT_MBYTE
3220 if (line != NULL && sp->conv.vc_type != CONV_NONE)
3221 {
3222 /* Convert the encoding of the script line. */
3223 s = string_convert(&sp->conv, line, NULL);
3224 if (s != NULL)
3225 {
3226 vim_free(line);
3227 line = s;
3228 }
3229 }
3230#endif
3231
3232#ifdef FEAT_EVAL
3233 /* Did we encounter a breakpoint? */
3234 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
3235 {
3236 dbg_breakpoint(sp->fname, sourcing_lnum);
3237 /* Find next breakpoint. */
3238 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3239 sp->dbg_tick = debug_tick;
3240 }
3241#endif
3242
3243 return line;
3244}
3245
3246 static char_u *
3247get_one_sourceline(sp)
3248 struct source_cookie *sp;
3249{
3250 garray_T ga;
3251 int len;
3252 int c;
3253 char_u *buf;
3254#ifdef USE_CRNL
3255 int has_cr; /* CR-LF found */
3256#endif
3257#ifdef USE_CR
3258 char_u *scan;
3259#endif
3260 int have_read = FALSE;
3261
3262 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003263 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264
3265 /*
3266 * Loop until there is a finished line (or end-of-file).
3267 */
3268 sourcing_lnum++;
3269 for (;;)
3270 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003271 /* make room to read at least 120 (more) characters */
3272 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 break;
3274 buf = (char_u *)ga.ga_data;
3275
3276#ifdef USE_CR
3277 if (sp->fileformat == EOL_MAC)
3278 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00003279 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3280 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003281 break;
3282 }
3283 else
3284#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00003285 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3286 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003288 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289#ifdef USE_CRNL
3290 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
3291 * CTRL-Z by its own, or after a NL. */
3292 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
3293 && sp->fileformat == EOL_DOS
3294 && buf[len - 1] == Ctrl_Z)
3295 {
3296 buf[len - 1] = NUL;
3297 break;
3298 }
3299#endif
3300
3301#ifdef USE_CR
3302 /* If the read doesn't stop on a new line, and there's
3303 * some CR then we assume a Mac format */
3304 if (sp->fileformat == EOL_UNKNOWN)
3305 {
3306 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
3307 sp->fileformat = EOL_MAC;
3308 else
3309 sp->fileformat = EOL_UNIX;
3310 }
3311
3312 if (sp->fileformat == EOL_MAC)
3313 {
3314 scan = vim_strchr(buf, '\r');
3315
3316 if (scan != NULL)
3317 {
3318 *scan = '\n';
3319 if (*(scan + 1) != 0)
3320 {
3321 *(scan + 1) = 0;
3322 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
3323 }
3324 }
3325 len = STRLEN(buf);
3326 }
3327#endif
3328
3329 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330 ga.ga_len = len;
3331
3332 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00003333 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 continue;
3335
3336 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
3337 {
3338#ifdef USE_CRNL
3339 has_cr = (len >= 2 && buf[len - 2] == '\r');
3340 if (sp->fileformat == EOL_UNKNOWN)
3341 {
3342 if (has_cr)
3343 sp->fileformat = EOL_DOS;
3344 else
3345 sp->fileformat = EOL_UNIX;
3346 }
3347
3348 if (sp->fileformat == EOL_DOS)
3349 {
3350 if (has_cr) /* replace trailing CR */
3351 {
3352 buf[len - 2] = '\n';
3353 --len;
3354 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 }
3356 else /* lines like ":map xx yy^M" will have failed */
3357 {
3358 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003359 {
3360 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003362 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 sp->error = TRUE;
3364 sp->fileformat = EOL_UNIX;
3365 }
3366 }
3367#endif
3368 /* The '\n' is escaped if there is an odd number of ^V's just
3369 * before it, first set "c" just before the 'V's and then check
3370 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
3371 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
3372 ;
3373 if ((len & 1) != (c & 1)) /* escaped NL, read more */
3374 {
3375 sourcing_lnum++;
3376 continue;
3377 }
3378
3379 buf[len - 1] = NUL; /* remove the NL */
3380 }
3381
3382 /*
3383 * Check for ^C here now and then, so recursive :so can be broken.
3384 */
3385 line_breakcheck();
3386 break;
3387 }
3388
3389 if (have_read)
3390 return (char_u *)ga.ga_data;
3391
3392 vim_free(ga.ga_data);
3393 return NULL;
3394}
3395
Bram Moolenaar05159a02005-02-26 23:04:13 +00003396#if defined(FEAT_PROFILE) || defined(PROTO)
3397/*
3398 * Called when starting to read a script line.
3399 * "sourcing_lnum" must be correct!
3400 * When skipping lines it may not actually be executed, but we won't find out
3401 * until later and we need to store the time now.
3402 */
3403 void
3404script_line_start()
3405{
3406 scriptitem_T *si;
3407 sn_prl_T *pp;
3408
3409 if (current_SID <= 0 || current_SID > script_items.ga_len)
3410 return;
3411 si = &SCRIPT_ITEM(current_SID);
3412 if (si->sn_prof_on && sourcing_lnum >= 1)
3413 {
3414 /* Grow the array before starting the timer, so that the time spend
3415 * here isn't counted. */
3416 ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
3417 si->sn_prl_idx = sourcing_lnum - 1;
3418 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
3419 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
3420 {
3421 /* Zero counters for a line that was not used before. */
3422 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
3423 pp->snp_count = 0;
3424 profile_zero(&pp->sn_prl_total);
3425 profile_zero(&pp->sn_prl_self);
3426 ++si->sn_prl_ga.ga_len;
3427 }
3428 si->sn_prl_execed = FALSE;
3429 profile_start(&si->sn_prl_start);
3430 profile_zero(&si->sn_prl_children);
3431 profile_get_wait(&si->sn_prl_wait);
3432 }
3433}
3434
3435/*
3436 * Called when actually executing a function line.
3437 */
3438 void
3439script_line_exec()
3440{
3441 scriptitem_T *si;
3442
3443 if (current_SID <= 0 || current_SID > script_items.ga_len)
3444 return;
3445 si = &SCRIPT_ITEM(current_SID);
3446 if (si->sn_prof_on && si->sn_prl_idx >= 0)
3447 si->sn_prl_execed = TRUE;
3448}
3449
3450/*
3451 * Called when done with a function line.
3452 */
3453 void
3454script_line_end()
3455{
3456 scriptitem_T *si;
3457 sn_prl_T *pp;
3458
3459 if (current_SID <= 0 || current_SID > script_items.ga_len)
3460 return;
3461 si = &SCRIPT_ITEM(current_SID);
3462 if (si->sn_prof_on && si->sn_prl_idx >= 0
3463 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
3464 {
3465 if (si->sn_prl_execed)
3466 {
3467 pp = &PRL_ITEM(si, si->sn_prl_idx);
3468 ++pp->snp_count;
3469 profile_end(&si->sn_prl_start);
3470 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
3471 profile_add(&pp->sn_prl_self, &si->sn_prl_start);
3472 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
3473 profile_sub(&pp->sn_prl_self, &si->sn_prl_children);
3474 }
3475 si->sn_prl_idx = -1;
3476 }
3477}
3478#endif
3479
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480/*
3481 * ":scriptencoding": Set encoding conversion for a sourced script.
3482 * Without the multi-byte feature it's simply ignored.
3483 */
3484/*ARGSUSED*/
3485 void
3486ex_scriptencoding(eap)
3487 exarg_T *eap;
3488{
3489#ifdef FEAT_MBYTE
3490 struct source_cookie *sp;
3491 char_u *name;
3492
3493 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
3494 {
3495 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
3496 return;
3497 }
3498
3499 if (*eap->arg != NUL)
3500 {
3501 name = enc_canonize(eap->arg);
3502 if (name == NULL) /* out of memory */
3503 return;
3504 }
3505 else
3506 name = eap->arg;
3507
3508 /* Setup for conversion from the specified encoding to 'encoding'. */
3509 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
3510 convert_setup(&sp->conv, name, p_enc);
3511
3512 if (name != eap->arg)
3513 vim_free(name);
3514#endif
3515}
3516
3517#if defined(FEAT_EVAL) || defined(PROTO)
3518/*
3519 * ":finish": Mark a sourced file as finished.
3520 */
3521 void
3522ex_finish(eap)
3523 exarg_T *eap;
3524{
3525 if (getline_equal(eap->getline, eap->cookie, getsourceline))
3526 do_finish(eap, FALSE);
3527 else
3528 EMSG(_("E168: :finish used outside of a sourced file"));
3529}
3530
3531/*
3532 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
3533 * Also called for a pending finish at the ":endtry" or after returning from
3534 * an extra do_cmdline(). "reanimate" is used in the latter case.
3535 */
3536 void
3537do_finish(eap, reanimate)
3538 exarg_T *eap;
3539 int reanimate;
3540{
3541 int idx;
3542
3543 if (reanimate)
3544 ((struct source_cookie *)getline_cookie(eap->getline,
3545 eap->cookie))->finished = FALSE;
3546
3547 /*
3548 * Cleanup (and inactivate) conditionals, but stop when a try conditional
3549 * not in its finally clause (which then is to be executed next) is found.
3550 * In this case, make the ":finish" pending for execution at the ":endtry".
3551 * Otherwise, finish normally.
3552 */
3553 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
3554 if (idx >= 0)
3555 {
3556 eap->cstack->cs_pending[idx] = CSTP_FINISH;
3557 report_make_pending(CSTP_FINISH, NULL);
3558 }
3559 else
3560 ((struct source_cookie *)getline_cookie(eap->getline,
3561 eap->cookie))->finished = TRUE;
3562}
3563
3564
3565/*
3566 * Return TRUE when a sourced file had the ":finish" command: Don't give error
3567 * message for missing ":endif".
3568 * Return FALSE when not sourcing a file.
3569 */
3570 int
3571source_finished(getline, cookie)
3572 char_u *(*getline) __ARGS((int, void *, int));
3573 void *cookie;
3574{
3575 return (getline_equal(getline, cookie, getsourceline)
3576 && ((struct source_cookie *)getline_cookie(
3577 getline, cookie))->finished);
3578}
3579#endif
3580
3581#if defined(FEAT_LISTCMDS) || defined(PROTO)
3582/*
3583 * ":checktime [buffer]"
3584 */
3585 void
3586ex_checktime(eap)
3587 exarg_T *eap;
3588{
3589 buf_T *buf;
3590 int save_no_check_timestamps = no_check_timestamps;
3591
3592 no_check_timestamps = 0;
3593 if (eap->addr_count == 0) /* default is all buffers */
3594 check_timestamps(FALSE);
3595 else
3596 {
3597 buf = buflist_findnr((int)eap->line2);
3598 if (buf != NULL) /* cannot happen? */
3599 (void)buf_check_timestamp(buf, FALSE);
3600 }
3601 no_check_timestamps = save_no_check_timestamps;
3602}
3603#endif
3604
3605#if defined(FEAT_PRINTER) || defined(PROTO)
3606/*
3607 * Printing code (Machine-independent.)
3608 * To implement printing on a platform, the following functions must be
3609 * defined:
3610 *
3611 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
3612 * Called once. Code should display printer dialogue (if appropriate) and
3613 * determine printer font and margin settings. Reset has_color if the printer
3614 * doesn't support colors at all.
3615 * Returns FAIL to abort.
3616 *
3617 * int mch_print_begin(prt_settings_T *settings)
3618 * Called to start the print job.
3619 * Return FALSE to abort.
3620 *
3621 * int mch_print_begin_page(char_u *msg)
3622 * Called at the start of each page.
3623 * "msg" indicates the progress of the print job, can be NULL.
3624 * Return FALSE to abort.
3625 *
3626 * int mch_print_end_page()
3627 * Called at the end of each page.
3628 * Return FALSE to abort.
3629 *
3630 * int mch_print_blank_page()
3631 * Called to generate a blank page for collated, duplex, multiple copy
3632 * document. Return FALSE to abort.
3633 *
3634 * void mch_print_end(prt_settings_T *psettings)
3635 * Called at normal end of print job.
3636 *
3637 * void mch_print_cleanup()
3638 * Called if print job ends normally or is abandoned. Free any memory, close
3639 * devices and handles. Also called when mch_print_begin() fails, but not
3640 * when mch_print_init() fails.
3641 *
3642 * void mch_print_set_font(int Bold, int Italic, int Underline);
3643 * Called whenever the font style changes.
3644 *
3645 * void mch_print_set_bg(long bgcol);
3646 * Called to set the background color for the following text. Parameter is an
3647 * RGB value.
3648 *
3649 * void mch_print_set_fg(long fgcol);
3650 * Called to set the foreground color for the following text. Parameter is an
3651 * RGB value.
3652 *
3653 * mch_print_start_line(int margin, int page_line)
3654 * Sets the current position at the start of line "page_line".
3655 * If margin is TRUE start in the left margin (for header and line number).
3656 *
3657 * int mch_print_text_out(char_u *p, int len);
3658 * Output one character of text p[len] at the current position.
3659 * Return TRUE if there is no room for another character in the same line.
3660 *
3661 * Note that the generic code has no idea of margins. The machine code should
3662 * simply make the page look smaller! The header and the line numbers are
3663 * printed in the margin.
3664 */
3665
3666#ifdef FEAT_SYN_HL
3667static const long_u cterm_color_8[8] =
3668{
3669 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
3670 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
3671};
3672
3673static const long_u cterm_color_16[16] =
3674{
3675 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
3676 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
3677 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
3678 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
3679};
3680
3681static int current_syn_id;
3682#endif
3683
3684#define PRCOLOR_BLACK (long_u)0
3685#define PRCOLOR_WHITE (long_u)0xFFFFFFL
3686
3687static int curr_italic;
3688static int curr_bold;
3689static int curr_underline;
3690static long_u curr_bg;
3691static long_u curr_fg;
3692static int page_count;
3693
3694/*
3695 * These values determine the print position on a page.
3696 */
3697typedef struct
3698{
3699 int lead_spaces; /* remaining spaces for a TAB */
3700 int print_pos; /* virtual column for computing TABs */
3701 colnr_T column; /* byte column */
3702 linenr_T file_line; /* line nr in the buffer */
3703 long_u bytes_printed; /* bytes printed so far */
3704 int ff; /* seen form feed character */
3705} prt_pos_T;
3706
3707#ifdef FEAT_SYN_HL
3708static long_u darken_rgb __ARGS((long_u rgb));
3709static long_u prt_get_term_color __ARGS((int colorindex));
3710#endif
3711static void prt_set_fg __ARGS((long_u fg));
3712static void prt_set_bg __ARGS((long_u bg));
3713static void prt_set_font __ARGS((int bold, int italic, int underline));
3714static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3715static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3716static void prt_message __ARGS((char_u *s));
3717static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3718static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3719
3720#ifdef FEAT_SYN_HL
3721/*
3722 * If using a dark background, the colors will probably be too bright to show
3723 * up well on white paper, so reduce their brightness.
3724 */
3725 static long_u
3726darken_rgb(rgb)
3727 long_u rgb;
3728{
3729 return ((rgb >> 17) << 16)
3730 + (((rgb & 0xff00) >> 9) << 8)
3731 + ((rgb & 0xff) >> 1);
3732}
3733
3734 static long_u
3735prt_get_term_color(colorindex)
3736 int colorindex;
3737{
3738 /* TODO: Should check for xterm with 88 or 256 colors. */
3739 if (t_colors > 8)
3740 return cterm_color_16[colorindex % 16];
3741 return cterm_color_8[colorindex % 8];
3742}
3743
3744 static void
3745prt_get_attr(hl_id, pattr, modec)
3746 int hl_id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003747 prt_text_attr_T *pattr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 int modec;
3749{
3750 int colorindex;
3751 long_u fg_color;
3752 long_u bg_color;
3753 char *color;
3754
3755 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3756 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3757 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003758 pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759
3760# ifdef FEAT_GUI
3761 if (gui.in_use)
3762 {
3763 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3764 if (bg_color == PRCOLOR_BLACK)
3765 bg_color = PRCOLOR_WHITE;
3766
3767 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3768 }
3769 else
3770# endif
3771 {
3772 bg_color = PRCOLOR_WHITE;
3773
3774 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3775 if (color == NULL)
3776 colorindex = 0;
3777 else
3778 colorindex = atoi(color);
3779
3780 if (colorindex >= 0 && colorindex < t_colors)
3781 fg_color = prt_get_term_color(colorindex);
3782 else
3783 fg_color = PRCOLOR_BLACK;
3784 }
3785
3786 if (fg_color == PRCOLOR_WHITE)
3787 fg_color = PRCOLOR_BLACK;
3788 else if (*p_bg == 'd')
3789 fg_color = darken_rgb(fg_color);
3790
3791 pattr->fg_color = fg_color;
3792 pattr->bg_color = bg_color;
3793}
3794#endif /* FEAT_SYN_HL */
3795
3796 static void
3797prt_set_fg(fg)
3798 long_u fg;
3799{
3800 if (fg != curr_fg)
3801 {
3802 curr_fg = fg;
3803 mch_print_set_fg(fg);
3804 }
3805}
3806
3807 static void
3808prt_set_bg(bg)
3809 long_u bg;
3810{
3811 if (bg != curr_bg)
3812 {
3813 curr_bg = bg;
3814 mch_print_set_bg(bg);
3815 }
3816}
3817
3818 static void
3819prt_set_font(bold, italic, underline)
3820 int bold;
3821 int italic;
3822 int underline;
3823{
3824 if (curr_bold != bold
3825 || curr_italic != italic
3826 || curr_underline != underline)
3827 {
3828 curr_underline = underline;
3829 curr_italic = italic;
3830 curr_bold = bold;
3831 mch_print_set_font(bold, italic, underline);
3832 }
3833}
3834
3835/*
3836 * Print the line number in the left margin.
3837 */
3838 static void
3839prt_line_number(psettings, page_line, lnum)
3840 prt_settings_T *psettings;
3841 int page_line;
3842 linenr_T lnum;
3843{
3844 int i;
3845 char_u tbuf[20];
3846
3847 prt_set_fg(psettings->number.fg_color);
3848 prt_set_bg(psettings->number.bg_color);
3849 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3850 mch_print_start_line(TRUE, page_line);
3851
3852 /* Leave two spaces between the number and the text; depends on
3853 * PRINT_NUMBER_WIDTH. */
3854 sprintf((char *)tbuf, "%6ld", (long)lnum);
3855 for (i = 0; i < 6; i++)
3856 (void)mch_print_text_out(&tbuf[i], 1);
3857
3858#ifdef FEAT_SYN_HL
3859 if (psettings->do_syntax)
3860 /* Set colors for next character. */
3861 current_syn_id = -1;
3862 else
3863#endif
3864 {
3865 /* Set colors and font back to normal. */
3866 prt_set_fg(PRCOLOR_BLACK);
3867 prt_set_bg(PRCOLOR_WHITE);
3868 prt_set_font(FALSE, FALSE, FALSE);
3869 }
3870}
3871
3872static linenr_T printer_page_num;
3873
3874 int
3875get_printer_page_num()
3876{
3877 return printer_page_num;
3878}
3879
3880/*
3881 * Get the currently effective header height.
3882 */
3883 int
3884prt_header_height()
3885{
3886 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3887 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3888 return 2;
3889}
3890
3891/*
3892 * Return TRUE if using a line number for printing.
3893 */
3894 int
3895prt_use_number()
3896{
3897 return (printer_opts[OPT_PRINT_NUMBER].present
3898 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3899}
3900
3901/*
3902 * Return the unit used in a margin item in 'printoptions'.
3903 * Returns PRT_UNIT_NONE if not recognized.
3904 */
3905 int
3906prt_get_unit(idx)
3907 int idx;
3908{
3909 int u = PRT_UNIT_NONE;
3910 int i;
3911 static char *(units[4]) = PRT_UNIT_NAMES;
3912
3913 if (printer_opts[idx].present)
3914 for (i = 0; i < 4; ++i)
3915 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3916 {
3917 u = i;
3918 break;
3919 }
3920 return u;
3921}
3922
3923/*
3924 * Print the page header.
3925 */
3926/*ARGSUSED*/
3927 static void
3928prt_header(psettings, pagenum, lnum)
3929 prt_settings_T *psettings;
3930 int pagenum;
3931 linenr_T lnum;
3932{
3933 int width = psettings->chars_per_line;
3934 int page_line;
3935 char_u *tbuf;
3936 char_u *p;
3937#ifdef FEAT_MBYTE
3938 int l;
3939#endif
3940
3941 /* Also use the space for the line number. */
3942 if (prt_use_number())
3943 width += PRINT_NUMBER_WIDTH;
3944
3945 tbuf = alloc(width + IOSIZE);
3946 if (tbuf == NULL)
3947 return;
3948
3949#ifdef FEAT_STL_OPT
3950 if (*p_header != NUL)
3951 {
3952 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3953
3954 /*
3955 * Need to (temporarily) set current line number and first/last line
3956 * number on the 'window'. Since we don't know how long the page is,
3957 * set the first and current line number to the top line, and guess
3958 * that the page length is 64.
3959 */
3960 tmp_lnum = curwin->w_cursor.lnum;
3961 tmp_topline = curwin->w_topline;
3962 tmp_botline = curwin->w_botline;
3963 curwin->w_cursor.lnum = lnum;
3964 curwin->w_topline = lnum;
3965 curwin->w_botline = lnum + 63;
3966 printer_page_num = pagenum;
3967
3968 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3969 p_header, ' ', width, NULL);
3970
3971 /* Reset line numbers */
3972 curwin->w_cursor.lnum = tmp_lnum;
3973 curwin->w_topline = tmp_topline;
3974 curwin->w_botline = tmp_botline;
3975 }
3976 else
3977#endif
3978 sprintf((char *)tbuf, _("Page %d"), pagenum);
3979
3980 prt_set_fg(PRCOLOR_BLACK);
3981 prt_set_bg(PRCOLOR_WHITE);
3982 prt_set_font(TRUE, FALSE, FALSE);
3983
3984 /* Use a negative line number to indicate printing in the top margin. */
3985 page_line = 0 - prt_header_height();
3986 mch_print_start_line(TRUE, page_line);
3987 for (p = tbuf; *p != NUL; )
3988 {
3989 if (mch_print_text_out(p,
3990#ifdef FEAT_MBYTE
3991 (l = (*mb_ptr2len_check)(p))
3992#else
3993 1
3994#endif
3995 ))
3996 {
3997 ++page_line;
3998 if (page_line >= 0) /* out of room in header */
3999 break;
4000 mch_print_start_line(TRUE, page_line);
4001 }
4002#ifdef FEAT_MBYTE
4003 p += l;
4004#else
4005 p++;
4006#endif
4007 }
4008
4009 vim_free(tbuf);
4010
4011#ifdef FEAT_SYN_HL
4012 if (psettings->do_syntax)
4013 /* Set colors for next character. */
4014 current_syn_id = -1;
4015 else
4016#endif
4017 {
4018 /* Set colors and font back to normal. */
4019 prt_set_fg(PRCOLOR_BLACK);
4020 prt_set_bg(PRCOLOR_WHITE);
4021 prt_set_font(FALSE, FALSE, FALSE);
4022 }
4023}
4024
4025/*
4026 * Display a print status message.
4027 */
4028 static void
4029prt_message(s)
4030 char_u *s;
4031{
4032 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
4033 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
4034 out_flush();
4035}
4036
4037 void
4038ex_hardcopy(eap)
4039 exarg_T *eap;
4040{
4041 linenr_T lnum;
4042 int collated_copies, uncollated_copies;
4043 prt_settings_T settings;
4044 long_u bytes_to_print = 0;
4045 int page_line;
4046 int jobsplit;
4047 int id;
4048
4049 memset(&settings, 0, sizeof(prt_settings_T));
4050 settings.has_color = TRUE;
4051
4052# ifdef FEAT_POSTSCRIPT
4053 if (*eap->arg == '>')
4054 {
4055 char_u *errormsg = NULL;
4056
4057 /* Expand things like "%.ps". */
4058 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
4059 {
4060 if (errormsg != NULL)
4061 EMSG(errormsg);
4062 return;
4063 }
4064 settings.outfile = skipwhite(eap->arg + 1);
4065 }
4066 else if (*eap->arg != NUL)
4067 settings.arguments = eap->arg;
4068# endif
4069
4070 /*
4071 * Initialise for printing. Ask the user for settings, unless forceit is
4072 * set.
4073 * The mch_print_init() code should set up margins if applicable. (It may
4074 * not be a real printer - for example the engine might generate HTML or
4075 * PS.)
4076 */
4077 if (mch_print_init(&settings,
4078 curbuf->b_fname == NULL
4079 ? (char_u *)buf_spname(curbuf)
4080 : curbuf->b_sfname == NULL
4081 ? curbuf->b_fname
4082 : curbuf->b_sfname,
4083 eap->forceit) == FAIL)
4084 return;
4085
4086#ifdef FEAT_SYN_HL
4087# ifdef FEAT_GUI
4088 if (gui.in_use)
4089 settings.modec = 'g';
4090 else
4091# endif
4092 if (t_colors > 1)
4093 settings.modec = 'c';
4094 else
4095 settings.modec = 't';
4096
4097 if (!syntax_present(curbuf))
4098 settings.do_syntax = FALSE;
4099 else if (printer_opts[OPT_PRINT_SYNTAX].present
4100 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
4101 settings.do_syntax =
4102 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
4103 else
4104 settings.do_syntax = settings.has_color;
4105#endif
4106
4107 /* Set up printing attributes for line numbers */
4108 settings.number.fg_color = PRCOLOR_BLACK;
4109 settings.number.bg_color = PRCOLOR_WHITE;
4110 settings.number.bold = FALSE;
4111 settings.number.italic = TRUE;
4112 settings.number.underline = FALSE;
4113#ifdef FEAT_SYN_HL
4114 /*
4115 * Syntax highlighting of line numbers.
4116 */
4117 if (prt_use_number() && settings.do_syntax)
4118 {
4119 id = syn_name2id((char_u *)"LineNr");
4120 if (id > 0)
4121 id = syn_get_final_id(id);
4122
4123 prt_get_attr(id, &settings.number, settings.modec);
4124 }
4125#endif /* FEAT_SYN_HL */
4126
4127 /*
4128 * Estimate the total lines to be printed
4129 */
4130 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
4131 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
4132 if (bytes_to_print == 0)
4133 {
4134 MSG(_("No text to be printed"));
4135 goto print_fail_no_begin;
4136 }
4137
4138 /* Set colors and font to normal. */
4139 curr_bg = (long_u)0xffffffffL;
4140 curr_fg = (long_u)0xffffffffL;
4141 curr_italic = MAYBE;
4142 curr_bold = MAYBE;
4143 curr_underline = MAYBE;
4144
4145 prt_set_fg(PRCOLOR_BLACK);
4146 prt_set_bg(PRCOLOR_WHITE);
4147 prt_set_font(FALSE, FALSE, FALSE);
4148#ifdef FEAT_SYN_HL
4149 current_syn_id = -1;
4150#endif
4151
4152 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
4153 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
4154
4155 if (!mch_print_begin(&settings))
4156 goto print_fail_no_begin;
4157
4158 /*
4159 * Loop over collated copies: 1 2 3, 1 2 3, ...
4160 */
4161 page_count = 0;
4162 for (collated_copies = 0;
4163 collated_copies < settings.n_collated_copies;
4164 collated_copies++)
4165 {
4166 prt_pos_T prtpos; /* current print position */
4167 prt_pos_T page_prtpos; /* print position at page start */
4168 int side;
4169
4170 memset(&page_prtpos, 0, sizeof(prt_pos_T));
4171 page_prtpos.file_line = eap->line1;
4172 prtpos = page_prtpos;
4173
4174 if (jobsplit && collated_copies > 0)
4175 {
4176 /* Splitting jobs: Stop a previous job and start a new one. */
4177 mch_print_end(&settings);
4178 if (!mch_print_begin(&settings))
4179 goto print_fail_no_begin;
4180 }
4181
4182 /*
4183 * Loop over all pages in the print job: 1 2 3 ...
4184 */
4185 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
4186 {
4187 /*
4188 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
4189 * For duplex: 12 12 12 34 34 34, ...
4190 */
4191 for (uncollated_copies = 0;
4192 uncollated_copies < settings.n_uncollated_copies;
4193 uncollated_copies++)
4194 {
4195 /* Set the print position to the start of this page. */
4196 prtpos = page_prtpos;
4197
4198 /*
4199 * Do front and rear side of a page.
4200 */
4201 for (side = 0; side <= settings.duplex; ++side)
4202 {
4203 /*
4204 * Print one page.
4205 */
4206
4207 /* Check for interrupt character every page. */
4208 ui_breakcheck();
4209 if (got_int || settings.user_abort)
4210 goto print_fail;
4211
4212 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
4213 page_count + 1 + side,
4214 prtpos.bytes_printed > 1000000
4215 ? (int)(prtpos.bytes_printed /
4216 (bytes_to_print / 100))
4217 : (int)((prtpos.bytes_printed * 100)
4218 / bytes_to_print));
4219 if (!mch_print_begin_page(IObuff))
4220 goto print_fail;
4221
4222 if (settings.n_collated_copies > 1)
4223 sprintf((char *)IObuff + STRLEN(IObuff),
4224 _(" Copy %d of %d"),
4225 collated_copies + 1,
4226 settings.n_collated_copies);
4227 prt_message(IObuff);
4228
4229 /*
4230 * Output header if required
4231 */
4232 if (prt_header_height() > 0)
4233 prt_header(&settings, page_count + 1 + side,
4234 prtpos.file_line);
4235
4236 for (page_line = 0; page_line < settings.lines_per_page;
4237 ++page_line)
4238 {
4239 prtpos.column = hardcopy_line(&settings,
4240 page_line, &prtpos);
4241 if (prtpos.column == 0)
4242 {
4243 /* finished a file line */
4244 prtpos.bytes_printed +=
4245 STRLEN(skipwhite(ml_get(prtpos.file_line)));
4246 if (++prtpos.file_line > eap->line2)
4247 break; /* reached the end */
4248 }
4249 else if (prtpos.ff)
4250 {
4251 /* Line had a formfeed in it - start new page but
4252 * stay on the current line */
4253 break;
4254 }
4255 }
4256
4257 if (!mch_print_end_page())
4258 goto print_fail;
4259 if (prtpos.file_line > eap->line2)
4260 break; /* reached the end */
4261 }
4262
4263 /*
4264 * Extra blank page for duplexing with odd number of pages and
4265 * more copies to come.
4266 */
4267 if (prtpos.file_line > eap->line2 && settings.duplex
4268 && side == 0
4269 && uncollated_copies + 1 < settings.n_uncollated_copies)
4270 {
4271 if (!mch_print_blank_page())
4272 goto print_fail;
4273 }
4274 }
4275 if (settings.duplex && prtpos.file_line <= eap->line2)
4276 ++page_count;
4277
4278 /* Remember the position where the next page starts. */
4279 page_prtpos = prtpos;
4280 }
4281
Bram Moolenaar555b2802005-05-19 21:08:39 +00004282 vim_snprintf((char *)IObuff, IOSIZE, _("Printed: %s"),
4283 settings.jobname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 prt_message(IObuff);
4285 }
4286
4287print_fail:
4288 if (got_int || settings.user_abort)
4289 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00004290 sprintf((char *)IObuff, "%s", _("Printing aborted"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 prt_message(IObuff);
4292 }
4293 mch_print_end(&settings);
4294
4295print_fail_no_begin:
4296 mch_print_cleanup();
4297}
4298
4299/*
4300 * Print one page line.
4301 * Return the next column to print, or zero if the line is finished.
4302 */
4303 static colnr_T
4304hardcopy_line(psettings, page_line, ppos)
4305 prt_settings_T *psettings;
4306 int page_line;
4307 prt_pos_T *ppos;
4308{
4309 colnr_T col;
4310 char_u *line;
4311 int need_break = FALSE;
4312 int outputlen;
4313 int tab_spaces;
4314 long_u print_pos;
4315#ifdef FEAT_SYN_HL
4316 prt_text_attr_T attr;
4317 int id;
4318#endif
4319
4320 if (ppos->column == 0 || ppos->ff)
4321 {
4322 print_pos = 0;
4323 tab_spaces = 0;
4324 if (!ppos->ff && prt_use_number())
4325 prt_line_number(psettings, page_line, ppos->file_line);
4326 ppos->ff = FALSE;
4327 }
4328 else
4329 {
4330 /* left over from wrap halfway a tab */
4331 print_pos = ppos->print_pos;
4332 tab_spaces = ppos->lead_spaces;
4333 }
4334
4335 mch_print_start_line(0, page_line);
4336 line = ml_get(ppos->file_line);
4337
4338 /*
4339 * Loop over the columns until the end of the file line or right margin.
4340 */
4341 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
4342 {
4343 outputlen = 1;
4344#ifdef FEAT_MBYTE
4345 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
4346 outputlen = 1;
4347#endif
4348#ifdef FEAT_SYN_HL
4349 /*
4350 * syntax highlighting stuff.
4351 */
4352 if (psettings->do_syntax)
4353 {
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00004354 id = syn_get_id(ppos->file_line, col, 1, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 if (id > 0)
4356 id = syn_get_final_id(id);
4357 else
4358 id = 0;
4359 /* Get the line again, a multi-line regexp may invalidate it. */
4360 line = ml_get(ppos->file_line);
4361
4362 if (id != current_syn_id)
4363 {
4364 current_syn_id = id;
4365 prt_get_attr(id, &attr, psettings->modec);
4366 prt_set_font(attr.bold, attr.italic, attr.underline);
4367 prt_set_fg(attr.fg_color);
4368 prt_set_bg(attr.bg_color);
4369 }
4370 }
4371#endif /* FEAT_SYN_HL */
4372
4373 /*
4374 * Appropriately expand any tabs to spaces.
4375 */
4376 if (line[col] == TAB || tab_spaces != 0)
4377 {
4378 if (tab_spaces == 0)
4379 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
4380
4381 while (tab_spaces > 0)
4382 {
4383 need_break = mch_print_text_out((char_u *)" ", 1);
4384 print_pos++;
4385 tab_spaces--;
4386 if (need_break)
4387 break;
4388 }
4389 /* Keep the TAB if we didn't finish it. */
4390 if (need_break && tab_spaces > 0)
4391 break;
4392 }
4393 else if (line[col] == FF
4394 && printer_opts[OPT_PRINT_FORMFEED].present
4395 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
4396 == 'y')
4397 {
4398 ppos->ff = TRUE;
4399 need_break = 1;
4400 }
4401 else
4402 {
4403 need_break = mch_print_text_out(line + col, outputlen);
4404#ifdef FEAT_MBYTE
4405 if (has_mbyte)
4406 print_pos += (*mb_ptr2cells)(line + col);
4407 else
4408#endif
4409 print_pos++;
4410 }
4411 }
4412
4413 ppos->lead_spaces = tab_spaces;
4414 ppos->print_pos = print_pos;
4415
4416 /*
4417 * Start next line of file if we clip lines, or have reached end of the
4418 * line, unless we are doing a formfeed.
4419 */
4420 if (!ppos->ff
4421 && (line[col] == NUL
4422 || (printer_opts[OPT_PRINT_WRAP].present
4423 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
4424 == 'n')))
4425 return 0;
4426 return col;
4427}
4428
4429# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
4430
4431/*
4432 * PS printer stuff.
4433 *
4434 * Sources of information to help maintain the PS printing code:
4435 *
4436 * 1. PostScript Language Reference, 3rd Edition,
4437 * Addison-Wesley, 1999, ISBN 0-201-37922-8
4438 * 2. PostScript Language Program Design,
4439 * Addison-Wesley, 1988, ISBN 0-201-14396-8
4440 * 3. PostScript Tutorial and Cookbook,
4441 * Addison Wesley, 1985, ISBN 0-201-10179-3
4442 * 4. PostScript Language Document Structuring Conventions Specification,
4443 * version 3.0,
4444 * Adobe Technote 5001, 25th September 1992
4445 * 5. PostScript Printer Description File Format Specification, Version 4.3,
4446 * Adobe technote 5003, 9th February 1996
4447 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
4448 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00004449 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
4450 * Adobe Technote 5014, 8th October 1996
4451 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
4452 * Adoboe Technote 5094, 8th September, 2001
4453 * 9. CJKV Information Processing, 2nd Edition,
4454 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455 *
4456 * Some of these documents can be found in PDF form on Adobe's web site -
4457 * http://www.adobe.com
4458 */
4459
Bram Moolenaar8299df92004-07-10 09:47:34 +00004460#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
4461
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
4463#define PRT_PS_DEFAULT_FONTSIZE (10)
4464#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
4465
4466struct prt_mediasize_S
4467{
4468 char *name;
4469 float width; /* width and height in points for portrait */
4470 float height;
4471};
4472
4473#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
4474
4475static struct prt_mediasize_S prt_mediasize[] =
4476{
4477 {"A4", 595.0, 842.0},
4478 {"letter", 612.0, 792.0},
4479 {"10x14", 720.0, 1008.0},
4480 {"A3", 842.0, 1191.0},
4481 {"A5", 420.0, 595.0},
4482 {"B4", 729.0, 1032.0},
4483 {"B5", 516.0, 729.0},
4484 {"executive", 522.0, 756.0},
4485 {"folio", 595.0, 935.0},
4486 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
4487 {"legal", 612.0, 1008.0},
4488 {"quarto", 610.0, 780.0},
4489 {"statement", 396.0, 612.0},
4490 {"tabloid", 792.0, 1224.0}
4491};
4492
4493/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
4494struct prt_ps_font_S
4495{
4496 int wx;
4497 int uline_offset;
4498 int uline_width;
4499 int bbox_min_y;
4500 int bbox_max_y;
4501 char *(ps_fontname[4]);
4502};
4503
4504#define PRT_PS_FONT_ROMAN (0)
4505#define PRT_PS_FONT_BOLD (1)
4506#define PRT_PS_FONT_OBLIQUE (2)
4507#define PRT_PS_FONT_BOLDOBLIQUE (3)
4508
Bram Moolenaar8299df92004-07-10 09:47:34 +00004509/* Standard font metrics for Courier family */
4510static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511{
4512 600,
4513 -100, 50,
4514 -250, 805,
4515 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
4516};
4517
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004518#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004519/* Generic font metrics for multi-byte fonts */
4520static struct prt_ps_font_S prt_ps_mb_font =
4521{
4522 1000,
4523 -100, 50,
4524 -250, 805,
4525 {NULL, NULL, NULL, NULL}
4526};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004527#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00004528
4529/* Pointer to current font set being used */
4530static struct prt_ps_font_S* prt_ps_font;
4531
4532/* Structures to map user named encoding and mapping to PS equivalents for
4533 * building CID font name */
4534struct prt_ps_encoding_S
4535{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004536 char *encoding;
4537 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004538 int needs_charset;
4539};
4540
4541struct prt_ps_charset_S
4542{
4543 char *charset;
4544 char *cmap_charset;
4545 int has_charset;
4546};
4547
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004548#ifdef FEAT_MBYTE
4549
Bram Moolenaar8299df92004-07-10 09:47:34 +00004550#define CS_JIS_C_1978 (0x01)
4551#define CS_JIS_X_1983 (0x02)
4552#define CS_JIS_X_1990 (0x04)
4553#define CS_NEC (0x08)
4554#define CS_MSWINDOWS (0x10)
4555#define CS_CP932 (0x20)
4556#define CS_KANJITALK6 (0x40)
4557#define CS_KANJITALK7 (0x80)
4558
4559/* Japanese encodings and charsets */
4560static struct prt_ps_encoding_S j_encodings[] =
4561{
4562 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
4563 CS_NEC)},
4564 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
4565 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
4566 CS_KANJITALK6|CS_KANJITALK7)},
4567 {"cp932", "RKSJ", CS_JIS_X_1983},
4568 {"ucs-2", "UCS2", CS_JIS_X_1990},
4569 {"utf-8", "UTF8" , CS_JIS_X_1990}
4570};
4571static struct prt_ps_charset_S j_charsets[] =
4572{
4573 {"JIS_C_1978", "78", CS_JIS_C_1978},
4574 {"JIS_X_1983", NULL, CS_JIS_X_1983},
4575 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
4576 {"NEC", "Ext", CS_NEC},
4577 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
4578 {"CP932", "90ms", CS_JIS_X_1983},
4579 {"KANJITALK6", "83pv", CS_KANJITALK6},
4580 {"KANJITALK7", "90pv", CS_KANJITALK7}
4581};
4582
4583#define CS_GB_2312_80 (0x01)
4584#define CS_GBT_12345_90 (0x02)
4585#define CS_GBK2K (0x04)
4586#define CS_SC_MAC (0x08)
4587#define CS_GBT_90_MAC (0x10)
4588#define CS_GBK (0x20)
4589#define CS_SC_ISO10646 (0x40)
4590
4591/* Simplified Chinese encodings and charsets */
4592static struct prt_ps_encoding_S sc_encodings[] =
4593{
4594 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
4595 {"gb18030", NULL, CS_GBK2K},
4596 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
4597 CS_GBT_90_MAC)},
4598 {"gbk", "EUC", CS_GBK},
4599 {"ucs-2", "UCS2", CS_SC_ISO10646},
4600 {"utf-8", "UTF8", CS_SC_ISO10646}
4601};
4602static struct prt_ps_charset_S sc_charsets[] =
4603{
4604 {"GB_2312-80", "GB", CS_GB_2312_80},
4605 {"GBT_12345-90","GBT", CS_GBT_12345_90},
4606 {"MAC", "GBpc", CS_SC_MAC},
4607 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
4608 {"GBK", "GBK", CS_GBK},
4609 {"GB18030", "GBK2K", CS_GBK2K},
4610 {"ISO10646", "UniGB", CS_SC_ISO10646}
4611};
4612
4613#define CS_CNS_PLANE_1 (0x01)
4614#define CS_CNS_PLANE_2 (0x02)
4615#define CS_CNS_PLANE_1_2 (0x04)
4616#define CS_B5 (0x08)
4617#define CS_ETEN (0x10)
4618#define CS_HK_GCCS (0x20)
4619#define CS_HK_SCS (0x40)
4620#define CS_HK_SCS_ETEN (0x80)
4621#define CS_MTHKL (0x100)
4622#define CS_MTHKS (0x200)
4623#define CS_DLHKL (0x400)
4624#define CS_DLHKS (0x800)
4625#define CS_TC_ISO10646 (0x1000)
4626
4627/* Traditional Chinese encodings and charsets */
4628static struct prt_ps_encoding_S tc_encodings[] =
4629{
4630 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
4631 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
4632 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
4633 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
4634 CS_DLHKS)},
4635 {"cp950", "B5", CS_B5},
4636 {"ucs-2", "UCS2", CS_TC_ISO10646},
4637 {"utf-8", "UTF8", CS_TC_ISO10646},
4638 {"utf-16", "UTF16", CS_TC_ISO10646},
4639 {"utf-32", "UTF32", CS_TC_ISO10646}
4640};
4641static struct prt_ps_charset_S tc_charsets[] =
4642{
4643 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
4644 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
4645 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
4646 {"BIG5", NULL, CS_B5},
4647 {"CP950", NULL, CS_B5},
4648 {"ETEN", "ETen", CS_ETEN},
4649 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
4650 {"SCS", "HKscs", CS_HK_SCS},
4651 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
4652 {"MTHKL", "HKm471", CS_MTHKL},
4653 {"MTHKS", "HKm314", CS_MTHKS},
4654 {"DLHKL", "HKdla", CS_DLHKL},
4655 {"DLHKS", "HKdlb", CS_DLHKS},
4656 {"ISO10646", "UniCNS", CS_TC_ISO10646}
4657};
4658
4659#define CS_KR_X_1992 (0x01)
4660#define CS_KR_MAC (0x02)
4661#define CS_KR_X_1992_MS (0x04)
4662#define CS_KR_ISO10646 (0x08)
4663
4664/* Korean encodings and charsets */
4665static struct prt_ps_encoding_S k_encodings[] =
4666{
4667 {"iso-2022-kr", NULL, CS_KR_X_1992},
4668 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
4669 {"johab", "Johab", CS_KR_X_1992},
4670 {"cp1361", "Johab", CS_KR_X_1992},
4671 {"uhc", "UHC", CS_KR_X_1992_MS},
4672 {"cp949", "UHC", CS_KR_X_1992_MS},
4673 {"ucs-2", "UCS2", CS_KR_ISO10646},
4674 {"utf-8", "UTF8", CS_KR_ISO10646}
4675};
4676static struct prt_ps_charset_S k_charsets[] =
4677{
4678 {"KS_X_1992", "KSC", CS_KR_X_1992},
4679 {"CP1361", "KSC", CS_KR_X_1992},
4680 {"MAC", "KSCpc", CS_KR_MAC},
4681 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
4682 {"CP949", "KSCms", CS_KR_X_1992_MS},
4683 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
4684 {"ISO10646", "UniKS", CS_KR_ISO10646}
4685};
4686
4687/* Collections of encodings and charsets for multi-byte printing */
4688struct prt_ps_mbfont_S
4689{
4690 int num_encodings;
4691 struct prt_ps_encoding_S *encodings;
4692 int num_charsets;
4693 struct prt_ps_charset_S *charsets;
4694 char *ascii_enc;
4695 char *defcs;
4696};
4697
4698static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4699{
4700 {
4701 NUM_ELEMENTS(j_encodings),
4702 j_encodings,
4703 NUM_ELEMENTS(j_charsets),
4704 j_charsets,
4705 "jis_roman",
4706 "JIS_X_1983"
4707 },
4708 {
4709 NUM_ELEMENTS(sc_encodings),
4710 sc_encodings,
4711 NUM_ELEMENTS(sc_charsets),
4712 sc_charsets,
4713 "gb_roman",
4714 "GB_2312-80"
4715 },
4716 {
4717 NUM_ELEMENTS(tc_encodings),
4718 tc_encodings,
4719 NUM_ELEMENTS(tc_charsets),
4720 tc_charsets,
4721 "cns_roman",
4722 "BIG5"
4723 },
4724 {
4725 NUM_ELEMENTS(k_encodings),
4726 k_encodings,
4727 NUM_ELEMENTS(k_charsets),
4728 k_charsets,
4729 "ks_roman",
4730 "KS_X_1992"
4731 }
4732};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004733#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004734
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735struct prt_ps_resource_S
4736{
4737 char_u name[64];
4738 char_u filename[MAXPATHL + 1];
4739 int type;
4740 char_u title[256];
4741 char_u version[256];
4742};
4743
4744/* Types of PS resource file currently used */
4745#define PRT_RESOURCE_TYPE_PROCSET (0)
4746#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004747#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748
4749/* The PS prolog file version number has to match - if the prolog file is
4750 * updated, increment the number in the file and here. Version checking was
4751 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004752 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 * Table of VIM and prolog versions:
4754 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004755 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004757 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004759#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004760#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761
4762/* String versions of PS resource types - indexed by constants above so don't
4763 * re-order!
4764 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004765static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766{
4767 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004768 "encoding",
4769 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770};
4771
4772/* Strings to look for in a PS resource file */
4773#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4774#define PRT_RESOURCE_RESOURCE "Resource-"
4775#define PRT_RESOURCE_PROCSET "ProcSet"
4776#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004777#define PRT_RESOURCE_CMAP "CMap"
4778
4779
4780/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4781 * read more comments. */
4782#define PRT_DSC_MISC_TYPE (-1)
4783#define PRT_DSC_TITLE_TYPE (1)
4784#define PRT_DSC_VERSION_TYPE (2)
4785#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4786
4787#define PRT_DSC_TITLE "%%Title:"
4788#define PRT_DSC_VERSION "%%Version:"
4789#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4790
4791struct prt_dsc_comment_S
4792{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004793 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004794 int len;
4795 int type;
4796};
4797
4798struct prt_dsc_line_S
4799{
4800 int type;
4801 char_u *string;
4802 int len;
4803};
4804
4805
4806#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4807struct prt_dsc_comment_S prt_dsc_table[] =
4808{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004809 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004810 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004811 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004812 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004813 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004814};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815
4816static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4817static void prt_write_file __ARGS((char_u *buffer));
4818static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4819static void prt_write_string __ARGS((char *s));
4820static void prt_write_int __ARGS((int i));
4821static void prt_write_boolean __ARGS((int b));
4822static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4823static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4824static void prt_write_real __ARGS((double val, int prec));
4825static void prt_def_var __ARGS((char *name, double value, int prec));
4826static void prt_flush_buffer __ARGS((void));
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00004827static void prt_resource_name __ARGS((char_u *filename, void *cookie));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4829static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4830static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4831static void prt_dsc_start __ARGS((void));
4832static void prt_dsc_noarg __ARGS((char *comment));
4833static void prt_dsc_textline __ARGS((char *comment, char *text));
4834static void prt_dsc_text __ARGS((char *comment, char *text));
4835static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4836static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4837static 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 +00004838static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4839static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840static float to_device_units __ARGS((int idx, double physsize, int def_number));
4841static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4842static void prt_font_metrics __ARGS((int font_scale));
4843static int prt_get_cpl __ARGS((void));
4844static int prt_get_lpp __ARGS((void));
4845static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004846static int prt_resfile_next_line __ARGS((void));
4847static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4848static int prt_resfile_skip_nonws __ARGS((int offset));
4849static int prt_resfile_skip_ws __ARGS((int offset));
4850static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4851#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004852static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004853static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4854static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4855static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4856#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857
4858/*
4859 * Variables for the output PostScript file.
4860 */
4861static FILE *prt_ps_fd;
4862static int prt_file_error;
4863static char_u *prt_ps_file_name = NULL;
4864
4865/*
4866 * Various offsets and dimensions in default PostScript user space (points).
4867 * Used for text positioning calculations
4868 */
4869static float prt_page_width;
4870static float prt_page_height;
4871static float prt_left_margin;
4872static float prt_right_margin;
4873static float prt_top_margin;
4874static float prt_bottom_margin;
4875static float prt_line_height;
4876static float prt_first_line_height;
4877static float prt_char_width;
4878static float prt_number_width;
4879static float prt_bgcol_offset;
4880static float prt_pos_x_moveto = 0.0;
4881static float prt_pos_y_moveto = 0.0;
4882
4883/*
4884 * Various control variables used to decide when and how to change the
4885 * PostScript graphics state.
4886 */
4887static int prt_need_moveto;
4888static int prt_do_moveto;
4889static int prt_need_font;
4890static int prt_font;
4891static int prt_need_underline;
4892static int prt_underline;
4893static int prt_do_underline;
4894static int prt_need_fgcol;
4895static int prt_fgcol;
4896static int prt_need_bgcol;
4897static int prt_do_bgcol;
4898static int prt_bgcol;
4899static int prt_new_bgcol;
4900static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004901static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004903static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904
4905/*
4906 * Variables controlling physical printing.
4907 */
4908static int prt_media;
4909static int prt_portrait;
4910static int prt_num_copies;
4911static int prt_duplex;
4912static int prt_tumble;
4913static int prt_collate;
4914
4915/*
4916 * Buffers used when generating PostScript output
4917 */
4918static char_u prt_line_buffer[257];
4919static garray_T prt_ps_buffer;
4920
4921# ifdef FEAT_MBYTE
4922static int prt_do_conv;
4923static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004924
4925static int prt_out_mbyte;
4926static int prt_custom_cmap;
4927static char prt_cmap[80];
4928static int prt_use_courier;
4929static int prt_in_ascii;
4930static int prt_half_width;
4931static char *prt_ascii_encoding;
4932static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933# endif
4934
4935 static void
4936prt_write_file_raw_len(buffer, bytes)
4937 char_u *buffer;
4938 int bytes;
4939{
4940 if (!prt_file_error
4941 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4942 != (size_t)bytes)
4943 {
4944 EMSG(_("E455: Error writing to PostScript output file"));
4945 prt_file_error = TRUE;
4946 }
4947}
4948
4949 static void
4950prt_write_file(buffer)
4951 char_u *buffer;
4952{
4953 prt_write_file_len(buffer, STRLEN(buffer));
4954}
4955
4956 static void
4957prt_write_file_len(buffer, bytes)
4958 char_u *buffer;
4959 int bytes;
4960{
4961#ifdef EBCDIC
4962 ebcdic2ascii(buffer, bytes);
4963#endif
4964 prt_write_file_raw_len(buffer, bytes);
4965}
4966
4967/*
4968 * Write a string.
4969 */
4970 static void
4971prt_write_string(s)
4972 char *s;
4973{
Bram Moolenaar555b2802005-05-19 21:08:39 +00004974 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%s", s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975 prt_write_file(prt_line_buffer);
4976}
4977
4978/*
4979 * Write an int and a space.
4980 */
4981 static void
4982prt_write_int(i)
4983 int i;
4984{
4985 sprintf((char *)prt_line_buffer, "%d ", i);
4986 prt_write_file(prt_line_buffer);
4987}
4988
4989/*
4990 * Write a boolean and a space.
4991 */
4992 static void
4993prt_write_boolean(b)
4994 int b;
4995{
4996 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4997 prt_write_file(prt_line_buffer);
4998}
4999
5000/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005001 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 */
5003 static void
5004prt_def_font(new_name, encoding, height, font)
5005 char *new_name;
5006 char *encoding;
5007 int height;
5008 char *font;
5009{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005010 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5011 "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005012 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005013#ifdef FEAT_MBYTE
5014 if (prt_out_mbyte)
5015 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
5016 new_name, height, 500./prt_ps_courier_font.wx, new_name);
5017 else
5018#endif
Bram Moolenaar555b2802005-05-19 21:08:39 +00005019 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5020 "/%s %d /_%s ffs\n", new_name, height, new_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005021 prt_write_file(prt_line_buffer);
5022}
5023
Bram Moolenaar8299df92004-07-10 09:47:34 +00005024#ifdef FEAT_MBYTE
5025/*
5026 * Write a line to define the CID font.
5027 */
5028 static void
5029prt_def_cidfont(new_name, height, cidfont)
5030 char *new_name;
5031 int height;
5032 char *cidfont;
5033{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005034 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5035 "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005036 prt_write_file(prt_line_buffer);
Bram Moolenaar555b2802005-05-19 21:08:39 +00005037 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5038 "/%s %d /_%s ffs\n", new_name, height, new_name);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005039 prt_write_file(prt_line_buffer);
5040}
5041
5042/*
5043 * Write a line to define a duplicate of a CID font
5044 */
5045 static void
5046prt_dup_cidfont(original_name, new_name)
5047 char *original_name;
5048 char *new_name;
5049{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005050 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5051 "/%s %s d\n", new_name, original_name);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005052 prt_write_file(prt_line_buffer);
5053}
5054#endif
5055
Bram Moolenaar071d4272004-06-13 20:20:40 +00005056/*
5057 * Convert a real value into an integer and fractional part as integers, with
5058 * the fractional part being in the range [0,10^precision). The fractional part
5059 * is also rounded based on the precision + 1'th fractional digit.
5060 */
5061 static void
5062prt_real_bits(real, precision, pinteger, pfraction)
5063 double real;
5064 int precision;
5065 int *pinteger;
5066 int *pfraction;
5067{
5068 int i;
5069 int integer;
5070 float fraction;
5071
5072 integer = (int)real;
5073 fraction = (float)(real - integer);
5074 if (real < (double)integer)
5075 fraction = -fraction;
5076 for (i = 0; i < precision; i++)
5077 fraction *= 10.0;
5078
5079 *pinteger = integer;
5080 *pfraction = (int)(fraction + 0.5);
5081}
5082
5083/*
5084 * Write a real and a space. Save bytes if real value has no fractional part!
5085 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
5086 * what decimal point character to use, but PS always requires a '.'.
5087 */
5088 static void
5089prt_write_real(val, prec)
5090 double val;
5091 int prec;
5092{
5093 int integer;
5094 int fraction;
5095
5096 prt_real_bits(val, prec, &integer, &fraction);
5097 /* Emit integer part */
5098 sprintf((char *)prt_line_buffer, "%d", integer);
5099 prt_write_file(prt_line_buffer);
5100 /* Only emit fraction if necessary */
5101 if (fraction != 0)
5102 {
5103 /* Remove any trailing zeros */
5104 while ((fraction % 10) == 0)
5105 {
5106 prec--;
5107 fraction /= 10;
5108 }
5109 /* Emit fraction left padded with zeros */
5110 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
5111 prt_write_file(prt_line_buffer);
5112 }
5113 sprintf((char *)prt_line_buffer, " ");
5114 prt_write_file(prt_line_buffer);
5115}
5116
5117/*
5118 * Write a line to define a numeric variable.
5119 */
5120 static void
5121prt_def_var(name, value, prec)
5122 char *name;
5123 double value;
5124 int prec;
5125{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005126 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5127 "/%s ", name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 prt_write_file(prt_line_buffer);
5129 prt_write_real(value, prec);
5130 sprintf((char *)prt_line_buffer, "d\n");
5131 prt_write_file(prt_line_buffer);
5132}
5133
5134/* Convert size from font space to user space at current font scale */
5135#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
5136
5137 static void
5138prt_flush_buffer()
5139{
5140 if (prt_ps_buffer.ga_len > 0)
5141 {
5142 /* Any background color must be drawn first */
5143 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
5144 {
5145 int r, g, b;
5146
5147 if (prt_do_moveto)
5148 {
5149 prt_write_real(prt_pos_x_moveto, 2);
5150 prt_write_real(prt_pos_y_moveto, 2);
5151 prt_write_string("m\n");
5152 prt_do_moveto = FALSE;
5153 }
5154
5155 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005156 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 prt_write_real(prt_line_height, 2);
5158
5159 /* Lastly add the color of the background */
5160 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
5161 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
5162 b = prt_new_bgcol & 0xff;
5163 prt_write_real(r / 255.0, 3);
5164 prt_write_real(g / 255.0, 3);
5165 prt_write_real(b / 255.0, 3);
5166 prt_write_string("bg\n");
5167 }
5168 /* Draw underlines before the text as it makes it slightly easier to
5169 * find the starting point.
5170 */
5171 if (prt_do_underline)
5172 {
5173 if (prt_do_moveto)
5174 {
5175 prt_write_real(prt_pos_x_moveto, 2);
5176 prt_write_real(prt_pos_y_moveto, 2);
5177 prt_write_string("m\n");
5178 prt_do_moveto = FALSE;
5179 }
5180
Bram Moolenaar8299df92004-07-10 09:47:34 +00005181 /* Underline length of text run */
5182 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005183 prt_write_string("ul\n");
5184 }
5185 /* Draw the text
5186 * Note: we write text out raw - EBCDIC conversion is handled in the
5187 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005188#ifdef FEAT_MBYTE
5189 if (prt_out_mbyte)
5190 prt_write_string("<");
5191 else
5192#endif
5193 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005194 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005195#ifdef FEAT_MBYTE
5196 if (prt_out_mbyte)
5197 prt_write_string(">");
5198 else
5199#endif
5200 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005201 /* Add a moveto if need be and use the appropriate show procedure */
5202 if (prt_do_moveto)
5203 {
5204 prt_write_real(prt_pos_x_moveto, 2);
5205 prt_write_real(prt_pos_y_moveto, 2);
5206 /* moveto and a show */
5207 prt_write_string("ms\n");
5208 prt_do_moveto = FALSE;
5209 }
5210 else /* Simple show */
5211 prt_write_string("s\n");
5212
5213 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005214 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005215 }
5216}
5217
Bram Moolenaar071d4272004-06-13 20:20:40 +00005218
5219 static void
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005220prt_resource_name(filename, cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221 char_u *filename;
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005222 void *cookie;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005223{
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005224 char_u *resource_filename = cookie;
5225
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226 if (STRLEN(filename) >= MAXPATHL)
5227 *resource_filename = NUL;
5228 else
5229 STRCPY(resource_filename, filename);
5230}
5231
5232 static int
5233prt_find_resource(name, resource)
5234 char *name;
5235 struct prt_ps_resource_S *resource;
5236{
5237 char_u buffer[MAXPATHL + 1];
5238
5239 STRCPY(resource->name, name);
5240 /* Look for named resource file in runtimepath */
5241 STRCPY(buffer, "print");
5242 add_pathsep(buffer);
5243 STRCAT(buffer, name);
5244 STRCAT(buffer, ".ps");
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005245 resource->filename[0] = NUL;
5246 return (do_in_runtimepath(buffer, FALSE, prt_resource_name,
5247 resource->filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005248 && resource->filename[0] != NUL);
5249}
5250
5251/* PS CR and LF characters have platform independent values */
5252#define PSLF (0x0a)
5253#define PSCR (0x0d)
5254
Bram Moolenaar8299df92004-07-10 09:47:34 +00005255/* Static buffer to read initial comments in a resource file, some can have a
5256 * couple of KB of comments! */
5257#define PRT_FILE_BUFFER_LEN (2048)
5258struct prt_resfile_buffer_S
5259{
5260 char_u buffer[PRT_FILE_BUFFER_LEN];
5261 int len;
5262 int line_start;
5263 int line_end;
5264};
5265
5266static struct prt_resfile_buffer_S prt_resfile;
5267
5268 static int
5269prt_resfile_next_line()
5270{
5271 int index;
5272
5273 /* Move to start of next line and then find end of line */
5274 index = prt_resfile.line_end + 1;
5275 while (index < prt_resfile.len)
5276 {
5277 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
5278 != PSCR)
5279 break;
5280 index++;
5281 }
5282 prt_resfile.line_start = index;
5283
5284 while (index < prt_resfile.len)
5285 {
5286 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
5287 == PSCR)
5288 break;
5289 index++;
5290 }
5291 prt_resfile.line_end = index;
5292
5293 return (index < prt_resfile.len);
5294}
5295
5296 static int
5297prt_resfile_strncmp(offset, string, len)
5298 int offset;
5299 char *string;
5300 int len;
5301{
5302 /* Force not equal if string is longer than remainder of line */
5303 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
5304 return 1;
5305
5306 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
5307 string, len);
5308}
5309
5310 static int
5311prt_resfile_skip_nonws(offset)
5312 int offset;
5313{
5314 int index;
5315
5316 index = prt_resfile.line_start + offset;
5317 while (index < prt_resfile.line_end)
5318 {
5319 if (isspace(prt_resfile.buffer[index]))
5320 return index - prt_resfile.line_start;
5321 index++;
5322 }
5323 return -1;
5324}
5325
5326 static int
5327prt_resfile_skip_ws(offset)
5328 int offset;
5329{
5330 int index;
5331
5332 index = prt_resfile.line_start + offset;
5333 while (index < prt_resfile.line_end)
5334 {
5335 if (!isspace(prt_resfile.buffer[index]))
5336 return index - prt_resfile.line_start;
5337 index++;
5338 }
5339 return -1;
5340}
5341
5342/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
5343 * if a DSC comment is found, else false */
5344 static int
5345prt_next_dsc(p_dsc_line)
5346 struct prt_dsc_line_S *p_dsc_line;
5347{
5348 int comment;
5349 int offset;
5350
5351 /* Move to start of next line */
5352 if (!prt_resfile_next_line())
5353 return FALSE;
5354
5355 /* DSC comments always start %% */
5356 if (prt_resfile_strncmp(0, "%%", 2) != 0)
5357 return FALSE;
5358
5359 /* Find type of DSC comment */
5360 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
5361 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
5362 prt_dsc_table[comment].len) == 0)
5363 break;
5364
5365 if (comment != NUM_ELEMENTS(prt_dsc_table))
5366 {
5367 /* Return type of comment */
5368 p_dsc_line->type = prt_dsc_table[comment].type;
5369 offset = prt_dsc_table[comment].len;
5370 }
5371 else
5372 {
5373 /* Unrecognised DSC comment, skip to ws after comment leader */
5374 p_dsc_line->type = PRT_DSC_MISC_TYPE;
5375 offset = prt_resfile_skip_nonws(0);
5376 if (offset == -1)
5377 return FALSE;
5378 }
5379
5380 /* Skip ws to comment value */
5381 offset = prt_resfile_skip_ws(offset);
5382 if (offset == -1)
5383 return FALSE;
5384
5385 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
5386 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
5387
5388 return TRUE;
5389}
5390
5391/* Improved hand crafted parser to get the type, title, and version number of a
5392 * PS resource file so the file details can be added to the DSC header comments.
5393 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 static int
5395prt_open_resource(resource)
5396 struct prt_ps_resource_S *resource;
5397{
Bram Moolenaar8299df92004-07-10 09:47:34 +00005398 int offset;
5399 int seen_all;
5400 int seen_title;
5401 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005402 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005403 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404
5405 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5406 if (fd_resource == NULL)
5407 {
5408 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
5409 return FALSE;
5410 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005411 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412
5413 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005414 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
5415 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005416 if (ferror(fd_resource))
5417 {
5418 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5419 resource->filename);
5420 fclose(fd_resource);
5421 return FALSE;
5422 }
5423
Bram Moolenaar8299df92004-07-10 09:47:34 +00005424 prt_resfile.line_end = -1;
5425 prt_resfile.line_start = 0;
5426 if (!prt_resfile_next_line())
5427 return FALSE;
5428
5429 offset = 0;
5430
5431 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
5432 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 {
5434 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
5435 resource->filename);
5436 fclose(fd_resource);
5437 return FALSE;
5438 }
5439
5440 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005441 offset += STRLEN(PRT_RESOURCE_HEADER);
5442 offset = prt_resfile_skip_nonws(offset);
5443 if (offset == -1)
5444 return FALSE;
5445 offset = prt_resfile_skip_ws(offset);
5446 if (offset == -1)
5447 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448
Bram Moolenaar8299df92004-07-10 09:47:34 +00005449 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
5450 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005451 {
5452 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5453 resource->filename);
5454 fclose(fd_resource);
5455 return FALSE;
5456 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005457 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005458
5459 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005460 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
5461 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005462 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005463 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
5464 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005465 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005466 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
5467 STRLEN(PRT_RESOURCE_CMAP)) == 0)
5468 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005469 else
5470 {
5471 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5472 resource->filename);
5473 fclose(fd_resource);
5474 return FALSE;
5475 }
5476
Bram Moolenaar8299df92004-07-10 09:47:34 +00005477 /* Look for title and version of resource */
5478 resource->title[0] = '\0';
5479 resource->version[0] = '\0';
5480 seen_title = FALSE;
5481 seen_version = FALSE;
5482 seen_all = FALSE;
5483 while (!seen_all && prt_next_dsc(&dsc_line))
5484 {
5485 switch (dsc_line.type)
5486 {
5487 case PRT_DSC_TITLE_TYPE:
5488 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
5489 resource->title[dsc_line.len] = '\0';
5490 seen_title = TRUE;
5491 if (seen_version)
5492 seen_all = TRUE;
5493 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494
Bram Moolenaar8299df92004-07-10 09:47:34 +00005495 case PRT_DSC_VERSION_TYPE:
5496 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
5497 resource->version[dsc_line.len] = '\0';
5498 seen_version = TRUE;
5499 if (seen_title)
5500 seen_all = TRUE;
5501 break;
5502
5503 case PRT_DSC_ENDCOMMENTS_TYPE:
5504 /* Wont find title or resource after this comment, stop searching */
5505 seen_all = TRUE;
5506 break;
5507
5508 case PRT_DSC_MISC_TYPE:
5509 /* Not interested in whatever comment this line had */
5510 break;
5511 }
5512 }
5513
5514 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515 {
5516 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5517 resource->filename);
5518 fclose(fd_resource);
5519 return FALSE;
5520 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521
5522 fclose(fd_resource);
5523
5524 return TRUE;
5525}
5526
5527 static int
5528prt_check_resource(resource, version)
5529 struct prt_ps_resource_S *resource;
5530 char_u *version;
5531{
5532 /* Version number m.n should match, the revision number does not matter */
5533 if (STRNCMP(resource->version, version, STRLEN(version)))
5534 {
5535 EMSG2(_("E621: \"%s\" resource file has wrong version"),
5536 resource->name);
5537 return FALSE;
5538 }
5539
5540 /* Other checks to be added as needed */
5541 return TRUE;
5542}
5543
5544 static void
5545prt_dsc_start()
5546{
5547 prt_write_string("%!PS-Adobe-3.0\n");
5548}
5549
5550 static void
5551prt_dsc_noarg(comment)
5552 char *comment;
5553{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005554 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5555 "%%%%%s\n", comment);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556 prt_write_file(prt_line_buffer);
5557}
5558
5559 static void
5560prt_dsc_textline(comment, text)
5561 char *comment;
5562 char *text;
5563{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005564 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5565 "%%%%%s: %s\n", comment, text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566 prt_write_file(prt_line_buffer);
5567}
5568
5569 static void
5570prt_dsc_text(comment, text)
5571 char *comment;
5572 char *text;
5573{
5574 /* TODO - should scan 'text' for any chars needing escaping! */
Bram Moolenaar555b2802005-05-19 21:08:39 +00005575 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5576 "%%%%%s: (%s)\n", comment, text);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577 prt_write_file(prt_line_buffer);
5578}
5579
5580#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
5581
5582 static void
5583prt_dsc_ints(comment, count, ints)
5584 char *comment;
5585 int count;
5586 int *ints;
5587{
5588 int i;
5589
Bram Moolenaar555b2802005-05-19 21:08:39 +00005590 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5591 "%%%%%s:", comment);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 prt_write_file(prt_line_buffer);
5593
5594 for (i = 0; i < count; i++)
5595 {
5596 sprintf((char *)prt_line_buffer, " %d", ints[i]);
5597 prt_write_file(prt_line_buffer);
5598 }
5599
5600 prt_write_string("\n");
5601}
5602
5603 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00005604prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 char *comment; /* if NULL add to previous */
5606 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005607 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005609 if (comment != NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00005610 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5611 "%%%%%s: %s", comment, type);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 else
Bram Moolenaar555b2802005-05-19 21:08:39 +00005613 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5614 "%%%%+ %s", type);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005615 prt_write_file(prt_line_buffer);
5616
Bram Moolenaar555b2802005-05-19 21:08:39 +00005617 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5618 " %s\n", string);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005619 prt_write_file(prt_line_buffer);
5620}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621
Bram Moolenaar8299df92004-07-10 09:47:34 +00005622 static void
5623prt_dsc_font_resource(resource, ps_font)
5624 char *resource;
5625 struct prt_ps_font_S *ps_font;
5626{
5627 int i;
5628
5629 prt_dsc_resources(resource, "font",
5630 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
5631 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
5632 if (ps_font->ps_fontname[i] != NULL)
5633 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634}
5635
5636 static void
5637prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
5638 int duplex;
5639 int tumble;
5640 int collate;
5641 int color;
5642 int num_copies;
5643{
5644 /* Only output the comment if we need to.
5645 * Note: tumble is ignored if we are not duplexing
5646 */
5647 if (!(duplex || collate || color || (num_copies > 1)))
5648 return;
5649
5650 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
5651 prt_write_file(prt_line_buffer);
5652
5653 if (duplex)
5654 {
5655 prt_write_string(" duplex");
5656 if (tumble)
5657 prt_write_string("(tumble)");
5658 }
5659 if (collate)
5660 prt_write_string(" collate");
5661 if (color)
5662 prt_write_string(" color");
5663 if (num_copies > 1)
5664 {
5665 prt_write_string(" numcopies(");
5666 /* Note: no space wanted so dont use prt_write_int() */
5667 sprintf((char *)prt_line_buffer, "%d", num_copies);
5668 prt_write_file(prt_line_buffer);
5669 prt_write_string(")");
5670 }
5671 prt_write_string("\n");
5672}
5673
5674 static void
5675prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
5676 char *paper_name;
5677 double width;
5678 double height;
5679 double weight;
5680 char *colour;
5681 char *type;
5682{
Bram Moolenaar555b2802005-05-19 21:08:39 +00005683 vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
5684 "%%%%DocumentMedia: %s ", paper_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 prt_write_file(prt_line_buffer);
5686 prt_write_real(width, 2);
5687 prt_write_real(height, 2);
5688 prt_write_real(weight, 2);
5689 if (colour == NULL)
5690 prt_write_string("()");
5691 else
5692 prt_write_string(colour);
5693 prt_write_string(" ");
5694 if (type == NULL)
5695 prt_write_string("()");
5696 else
5697 prt_write_string(type);
5698 prt_write_string("\n");
5699}
5700
5701 void
5702mch_print_cleanup()
5703{
5704#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005705 if (prt_out_mbyte)
5706 {
5707 int i;
5708
5709 /* Free off all CID font names created, but first clear duplicate
5710 * pointers to the same string (when the same font is used for more than
5711 * one style).
5712 */
5713 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5714 {
5715 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5716 vim_free(prt_ps_mb_font.ps_fontname[i]);
5717 prt_ps_mb_font.ps_fontname[i] = NULL;
5718 }
5719 }
5720
Bram Moolenaar071d4272004-06-13 20:20:40 +00005721 if (prt_do_conv)
5722 {
5723 convert_setup(&prt_conv, NULL, NULL);
5724 prt_do_conv = FALSE;
5725 }
5726#endif
5727 if (prt_ps_fd != NULL)
5728 {
5729 fclose(prt_ps_fd);
5730 prt_ps_fd = NULL;
5731 prt_file_error = FALSE;
5732 }
5733 if (prt_ps_file_name != NULL)
5734 {
5735 vim_free(prt_ps_file_name);
5736 prt_ps_file_name = NULL;
5737 }
5738}
5739
5740 static float
5741to_device_units(idx, physsize, def_number)
5742 int idx;
5743 double physsize;
5744 int def_number;
5745{
5746 float ret;
5747 int u;
5748 int nr;
5749
5750 u = prt_get_unit(idx);
5751 if (u == PRT_UNIT_NONE)
5752 {
5753 u = PRT_UNIT_PERC;
5754 nr = def_number;
5755 }
5756 else
5757 nr = printer_opts[idx].number;
5758
5759 switch (u)
5760 {
5761 case PRT_UNIT_INCH:
5762 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5763 break;
5764 case PRT_UNIT_MM:
5765 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5766 break;
5767 case PRT_UNIT_POINT:
5768 ret = (float)nr;
5769 break;
5770 case PRT_UNIT_PERC:
5771 default:
5772 ret = (float)(physsize * nr) / 100;
5773 break;
5774 }
5775
5776 return ret;
5777}
5778
5779/*
5780 * Calculate margins for given width and height from printoptions settings.
5781 */
5782 static void
5783prt_page_margins(width, height, left, right, top, bottom)
5784 double width;
5785 double height;
5786 double *left;
5787 double *right;
5788 double *top;
5789 double *bottom;
5790{
5791 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5792 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5793 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5794 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5795}
5796
5797 static void
5798prt_font_metrics(font_scale)
5799 int font_scale;
5800{
5801 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005802 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803}
5804
5805
5806 static int
5807prt_get_cpl()
5808{
5809 if (prt_use_number())
5810 {
5811 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005812#ifdef FEAT_MBYTE
5813 /* If we are outputting multi-byte characters then line numbers will be
5814 * printed with half width characters
5815 */
5816 if (prt_out_mbyte)
5817 prt_number_width /= 2;
5818#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005819 prt_left_margin += prt_number_width;
5820 }
5821 else
5822 prt_number_width = 0.0;
5823
5824 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5825}
5826
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005827#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005828 static int
5829prt_build_cid_fontname(font, name, name_len)
5830 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005831 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005832 int name_len;
5833{
5834 char *fontname;
5835
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005836 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005837 if (fontname == NULL)
5838 return FALSE;
5839 STRNCPY(fontname, name, name_len);
5840 fontname[name_len] = '\0';
5841 prt_ps_mb_font.ps_fontname[font] = fontname;
5842
5843 return TRUE;
5844}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005845#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005846
Bram Moolenaar071d4272004-06-13 20:20:40 +00005847/*
5848 * Get number of lines of text that fit on a page (excluding the header).
5849 */
5850 static int
5851prt_get_lpp()
5852{
5853 int lpp;
5854
5855 /*
5856 * Calculate offset to lower left corner of background rect based on actual
5857 * font height (based on its bounding box) and the line height, handling the
5858 * case where the font height can exceed the line height.
5859 */
5860 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005861 prt_ps_font->bbox_min_y);
5862 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863 {
5864 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005865 (1000.0 - (prt_ps_font->bbox_max_y -
5866 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 }
5868
5869 /* Get height for topmost line based on background rect offset. */
5870 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5871
5872 /* Calculate lpp */
5873 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5874
5875 /* Adjust top margin if there is a header */
5876 prt_top_margin -= prt_line_height * prt_header_height();
5877
5878 return lpp - prt_header_height();
5879}
5880
Bram Moolenaar8299df92004-07-10 09:47:34 +00005881#ifdef FEAT_MBYTE
5882 static int
5883prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005884 char *p_encoding;
5885 struct prt_ps_mbfont_S *p_cmap;
5886 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005887{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005888 int mbenc;
5889 int enc_len;
5890 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005891
5892 *pp_mbenc = NULL;
5893 /* Look for recognised encoding */
5894 enc_len = STRLEN(p_encoding);
5895 p_mbenc = p_cmap->encodings;
5896 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5897 {
5898 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5899 {
5900 *pp_mbenc = p_mbenc;
5901 return TRUE;
5902 }
5903 p_mbenc++;
5904 }
5905 return FALSE;
5906}
5907
5908 static int
5909prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005910 char *p_charset;
5911 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005912 struct prt_ps_charset_S **pp_mbchar;
5913{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005914 int mbchar;
5915 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005916 struct prt_ps_charset_S *p_mbchar;
5917
5918 /* Look for recognised character set, using default if one is not given */
5919 if (*p_charset == NUL)
5920 p_charset = p_cmap->defcs;
5921 char_len = STRLEN(p_charset);
5922 p_mbchar = p_cmap->charsets;
5923 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5924 {
5925 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5926 {
5927 *pp_mbchar = p_mbchar;
5928 return TRUE;
5929 }
5930 p_mbchar++;
5931 }
5932 return FALSE;
5933}
5934#endif
5935
Bram Moolenaar071d4272004-06-13 20:20:40 +00005936/*ARGSUSED*/
5937 int
5938mch_print_init(psettings, jobname, forceit)
5939 prt_settings_T *psettings;
5940 char_u *jobname;
5941 int forceit;
5942{
5943 int i;
5944 char *paper_name;
5945 int paper_strlen;
5946 int fontsize;
5947 char_u *p;
5948 double left;
5949 double right;
5950 double top;
5951 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005952#ifdef FEAT_MBYTE
5953 int cmap;
5954 int pmcs_len;
5955 char_u *p_encoding;
5956 struct prt_ps_encoding_S *p_mbenc;
5957 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005958 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005959#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960
5961#if 0
5962 /*
5963 * TODO:
5964 * If "forceit" is false: pop up a dialog to select:
5965 * - printer name
5966 * - copies
5967 * - collated/uncollated
5968 * - duplex off/long side/short side
5969 * - paper size
5970 * - portrait/landscape
5971 * - font size
5972 *
5973 * If "forceit" is true: use the default printer and settings
5974 */
5975 if (forceit)
5976 s_pd.Flags |= PD_RETURNDEFAULT;
5977#endif
5978
5979 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005980 * Set up font and encoding.
5981 */
5982#ifdef FEAT_MBYTE
5983 p_encoding = enc_skip(p_penc);
5984 if (*p_encoding == NUL)
5985 p_encoding = enc_skip(p_enc);
5986
5987 /* Look for recognised multi-byte coding, and if the charset is recognised.
5988 * This is to cope with the fact that various unicode encodings are
5989 * supported in more than one of CJK. */
5990 p_mbenc = NULL;
5991 p_mbenc_first = NULL;
5992 p_mbchar = NULL;
5993 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005994 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5995 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005996 {
5997 if (p_mbenc_first == NULL)
5998 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005999 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
6000 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006001 break;
6002 }
6003
6004 /* Use first encoding matched if no charset matched */
6005 if (p_mbchar == NULL && p_mbenc_first != NULL)
6006 p_mbenc = p_mbenc_first;
6007
6008 prt_out_mbyte = (p_mbenc != NULL);
6009 if (prt_out_mbyte)
6010 {
6011 /* Build CMap name - will be same for all multi-byte fonts used */
6012 prt_cmap[0] = '\0';
6013
6014 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
6015
6016 if (!prt_custom_cmap)
6017 {
6018 /* Check encoding and character set are compatible */
6019 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
6020 {
6021 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
6022 return FALSE;
6023 }
6024
6025 /* Add charset name if not empty */
6026 if (p_mbchar->cmap_charset != NULL)
6027 {
6028 STRCAT(prt_cmap, p_mbchar->cmap_charset);
6029 STRCAT(prt_cmap, "-");
6030 }
6031 }
6032 else
6033 {
6034 /* Add custom CMap character set name */
6035 pmcs_len = STRLEN(p_pmcs);
6036 if (pmcs_len == 0)
6037 {
6038 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
6039 return FALSE;
6040 }
6041 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
6042 prt_cmap[pmcs_len] = '\0';
6043 STRCAT(prt_cmap, "-");
6044 }
6045
6046 /* CMap name ends with (optional) encoding name and -H for horizontal */
6047 if (p_mbenc->cmap_encoding != NULL)
6048 {
6049 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
6050 STRCAT(prt_cmap, "-");
6051 }
6052 STRCAT(prt_cmap, "H");
6053
6054 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
6055 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00006056 EMSG(_("E675: No default font specified for multi-byte printing."));
Bram Moolenaar8299df92004-07-10 09:47:34 +00006057 return FALSE;
6058 }
6059
6060 /* Derive CID font names with fallbacks if not defined */
6061 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
6062 mbfont_opts[OPT_MBFONT_REGULAR].string,
6063 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
6064 return FALSE;
6065 if (mbfont_opts[OPT_MBFONT_BOLD].present)
6066 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
6067 mbfont_opts[OPT_MBFONT_BOLD].string,
6068 mbfont_opts[OPT_MBFONT_BOLD].strlen))
6069 return FALSE;
6070 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
6071 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
6072 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
6073 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
6074 return FALSE;
6075 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
6076 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006077 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
6078 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006079 return FALSE;
6080
6081 /* Check if need to use Courier for ASCII code range, and if so pick up
6082 * the encoding to use */
6083 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
6084 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
6085 if (prt_use_courier)
6086 {
6087 /* Use national ASCII variant unless ASCII wanted */
6088 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
6089 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
6090 prt_ascii_encoding = "ascii";
6091 else
6092 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
6093 }
6094
6095 prt_ps_font = &prt_ps_mb_font;
6096 }
6097 else
6098#endif
6099 {
6100#ifdef FEAT_MBYTE
6101 prt_use_courier = FALSE;
6102#endif
6103 prt_ps_font = &prt_ps_courier_font;
6104 }
6105
6106 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 * Find the size of the paper and set the margins.
6108 */
6109 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
6110 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
6111 if (printer_opts[OPT_PRINT_PAPER].present)
6112 {
6113 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
6114 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
6115 }
6116 else
6117 {
6118 paper_name = "A4";
6119 paper_strlen = 2;
6120 }
6121 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
6122 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
6123 && STRNICMP(prt_mediasize[i].name, paper_name,
6124 paper_strlen) == 0)
6125 break;
6126 if (i == PRT_MEDIASIZE_LEN)
6127 i = 0;
6128 prt_media = i;
6129
6130 /*
6131 * Set PS pagesize based on media dimensions and print orientation.
6132 * Note: Media and page sizes have defined meanings in PostScript and should
6133 * be kept distinct. Media is the paper (or transparency, or ...) that is
6134 * printed on, whereas the page size is the area that the PostScript
6135 * interpreter renders into.
6136 */
6137 if (prt_portrait)
6138 {
6139 prt_page_width = prt_mediasize[i].width;
6140 prt_page_height = prt_mediasize[i].height;
6141 }
6142 else
6143 {
6144 prt_page_width = prt_mediasize[i].height;
6145 prt_page_height = prt_mediasize[i].width;
6146 }
6147
6148 /*
6149 * Set PS page margins based on the PS pagesize, not the mediasize - this
6150 * needs to be done before the cpl and lpp are calculated.
6151 */
6152 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
6153 &bottom);
6154 prt_left_margin = (float)left;
6155 prt_right_margin = (float)right;
6156 prt_top_margin = (float)top;
6157 prt_bottom_margin = (float)bottom;
6158
6159 /*
6160 * Set up the font size.
6161 */
6162 fontsize = PRT_PS_DEFAULT_FONTSIZE;
6163 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
6164 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
6165 fontsize = atoi((char *)p + 2);
6166 prt_font_metrics(fontsize);
6167
6168 /*
6169 * Return the number of characters per line, and lines per page for the
6170 * generic print code.
6171 */
6172 psettings->chars_per_line = prt_get_cpl();
6173 psettings->lines_per_page = prt_get_lpp();
6174
6175 /* Catch margin settings that leave no space for output! */
6176 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
6177 return FAIL;
6178
6179 /*
6180 * Sort out the number of copies to be printed. PS by default will do
6181 * uncollated copies for you, so once we know how many uncollated copies are
6182 * wanted cache it away and lie to the generic code that we only want one
6183 * uncollated copy.
6184 */
6185 psettings->n_collated_copies = 1;
6186 psettings->n_uncollated_copies = 1;
6187 prt_num_copies = 1;
6188 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
6189 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
6190 if (prt_collate)
6191 {
6192 /* TODO: Get number of collated copies wanted. */
6193 psettings->n_collated_copies = 1;
6194 }
6195 else
6196 {
6197 /* TODO: Get number of uncollated copies wanted and update the cached
6198 * count.
6199 */
6200 prt_num_copies = 1;
6201 }
6202
6203 psettings->jobname = jobname;
6204
6205 /*
6206 * Set up printer duplex and tumble based on Duplex option setting - default
6207 * is long sided duplex printing (i.e. no tumble).
6208 */
6209 prt_duplex = TRUE;
6210 prt_tumble = FALSE;
6211 psettings->duplex = 1;
6212 if (printer_opts[OPT_PRINT_DUPLEX].present)
6213 {
6214 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
6215 {
6216 prt_duplex = FALSE;
6217 psettings->duplex = 0;
6218 }
6219 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
6220 == 0)
6221 prt_tumble = TRUE;
6222 }
6223
6224 /* For now user abort not supported */
6225 psettings->user_abort = 0;
6226
6227 /* If the user didn't specify a file name, use a temp file. */
6228 if (psettings->outfile == NULL)
6229 {
6230 prt_ps_file_name = vim_tempname('p');
6231 if (prt_ps_file_name == NULL)
6232 {
6233 EMSG(_(e_notmp));
6234 return FAIL;
6235 }
6236 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
6237 }
6238 else
6239 {
6240 p = expand_env_save(psettings->outfile);
6241 if (p != NULL)
6242 {
6243 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
6244 vim_free(p);
6245 }
6246 }
6247 if (prt_ps_fd == NULL)
6248 {
6249 EMSG(_("E324: Can't open PostScript output file"));
6250 mch_print_cleanup();
6251 return FAIL;
6252 }
6253
Bram Moolenaar8299df92004-07-10 09:47:34 +00006254 prt_bufsiz = psettings->chars_per_line;
6255#ifdef FEAT_MBYTE
6256 if (prt_out_mbyte)
6257 prt_bufsiz *= 2;
6258#endif
6259 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006260
6261 prt_page_num = 0;
6262
6263 prt_attribute_change = FALSE;
6264 prt_need_moveto = FALSE;
6265 prt_need_font = FALSE;
6266 prt_need_fgcol = FALSE;
6267 prt_need_bgcol = FALSE;
6268 prt_need_underline = FALSE;
6269
6270 prt_file_error = FALSE;
6271
6272 return OK;
6273}
6274
6275 static int
6276prt_add_resource(resource)
6277 struct prt_ps_resource_S *resource;
6278{
6279 FILE* fd_resource;
6280 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006281 size_t bytes_read;
6282
6283 fd_resource = mch_fopen((char *)resource->filename, READBIN);
6284 if (fd_resource == NULL)
6285 {
6286 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
6287 return FALSE;
6288 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006289 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
6290 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
6292 prt_dsc_textline("BeginDocument", (char *)resource->filename);
6293
6294 for (;;)
6295 {
6296 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
6297 sizeof(resource_buffer), fd_resource);
6298 if (ferror(fd_resource))
6299 {
6300 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
6301 resource->filename);
6302 fclose(fd_resource);
6303 return FALSE;
6304 }
6305 if (bytes_read == 0)
6306 break;
6307 prt_write_file_raw_len(resource_buffer, bytes_read);
6308 if (prt_file_error)
6309 {
6310 fclose(fd_resource);
6311 return FALSE;
6312 }
6313 }
6314 fclose(fd_resource);
6315
6316 prt_dsc_noarg("EndDocument");
6317
6318 prt_dsc_noarg("EndResource");
6319
6320 return TRUE;
6321}
6322
6323 int
6324mch_print_begin(psettings)
6325 prt_settings_T *psettings;
6326{
6327 time_t now;
6328 int bbox[4];
6329 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 double left;
6331 double right;
6332 double top;
6333 double bottom;
6334 struct prt_ps_resource_S res_prolog;
6335 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006336 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006337 char_u *p_encoding;
6338#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006339 struct prt_ps_resource_S res_cidfont;
6340 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341#endif
6342
6343 /*
6344 * PS DSC Header comments - no PS code!
6345 */
6346 prt_dsc_start();
6347 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006348 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006349 STRCPY(buffer, "Unknown");
6350 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351 prt_dsc_textline("Creator", VIM_VERSION_LONG);
6352 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
6353 now = time(NULL);
6354 p_time = ctime(&now);
6355 /* Note: ctime() adds a \n so we have to remove it :-( */
6356 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
6357 prt_dsc_textline("CreationDate", p_time);
6358 prt_dsc_textline("DocumentData", "Clean8Bit");
6359 prt_dsc_textline("Orientation", "Portrait");
6360 prt_dsc_atend("Pages");
6361 prt_dsc_textline("PageOrder", "Ascend");
6362 /* The bbox does not change with orientation - it is always in the default
6363 * user coordinate system! We have to recalculate right and bottom
6364 * coordinates based on the font metrics for the bbox to be accurate. */
6365 prt_page_margins(prt_mediasize[prt_media].width,
6366 prt_mediasize[prt_media].height,
6367 &left, &right, &top, &bottom);
6368 bbox[0] = (int)left;
6369 if (prt_portrait)
6370 {
6371 /* In portrait printing the fixed point is the top left corner so we
6372 * derive the bbox from that point. We have the expected cpl chars
6373 * across the media and lpp lines down the media.
6374 */
6375 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
6376 * prt_line_height);
6377 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
6378 + 0.5);
6379 bbox[3] = (int)(top + 0.5);
6380 }
6381 else
6382 {
6383 /* In landscape printing the fixed point is the bottom left corner so we
6384 * derive the bbox from that point. We have lpp chars across the media
6385 * and cpl lines up the media.
6386 */
6387 bbox[1] = (int)bottom;
6388 bbox[2] = (int)(left + ((psettings->lines_per_page
6389 + prt_header_height()) * prt_line_height) + 0.5);
6390 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
6391 + 0.5);
6392 }
6393 prt_dsc_ints("BoundingBox", 4, bbox);
6394 /* The media width and height does not change with landscape printing! */
6395 prt_dsc_docmedia(prt_mediasize[prt_media].name,
6396 prt_mediasize[prt_media].width,
6397 prt_mediasize[prt_media].height,
6398 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006399 /* Define fonts needed */
6400#ifdef FEAT_MBYTE
6401 if (!prt_out_mbyte || prt_use_courier)
6402#endif
6403 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
6404#ifdef FEAT_MBYTE
6405 if (prt_out_mbyte)
6406 {
6407 prt_dsc_font_resource((prt_use_courier ? NULL
6408 : "DocumentNeededResources"), &prt_ps_mb_font);
6409 if (!prt_custom_cmap)
6410 prt_dsc_resources(NULL, "cmap", prt_cmap);
6411 }
6412#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413
Bram Moolenaar8299df92004-07-10 09:47:34 +00006414 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 if (!prt_find_resource("prolog", &res_prolog))
6416 {
6417 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
6418 return FALSE;
6419 }
6420 if (!prt_open_resource(&res_prolog))
6421 return FALSE;
6422 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
6423 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006424#ifdef FEAT_MBYTE
6425 if (prt_out_mbyte)
6426 {
6427 /* Look for required version of multi-byte printing procset */
6428 if (!prt_find_resource("cidfont", &res_cidfont))
6429 {
6430 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
6431 return FALSE;
6432 }
6433 if (!prt_open_resource(&res_cidfont))
6434 return FALSE;
6435 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
6436 return FALSE;
6437 }
6438#endif
6439
Bram Moolenaar071d4272004-06-13 20:20:40 +00006440 /* Find an encoding to use for printing.
6441 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
6442 * that cannot be found then default to "latin1".
6443 * Note: VIM specific encoding header is always skipped.
6444 */
6445#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006446 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006447 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006449 p_encoding = enc_skip(p_penc);
6450 if (*p_encoding == NUL
6451 || !prt_find_resource((char *)p_encoding, &res_encoding))
6452 {
6453 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006455 int props;
6456
6457 p_encoding = enc_skip(p_enc);
6458 props = enc_canon_props(p_encoding);
6459 if (!(props & ENC_8BIT)
6460 || !prt_find_resource((char *)p_encoding, &res_encoding))
6461 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006463 {
6464 /* Use latin1 as default printing encoding */
6465 p_encoding = (char_u *)"latin1";
6466 if (!prt_find_resource((char *)p_encoding, &res_encoding))
6467 {
6468 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
6469 p_encoding);
6470 return FALSE;
6471 }
6472 }
6473 }
6474 if (!prt_open_resource(&res_encoding))
6475 return FALSE;
6476 /* For the moment there are no checks on encoding resource files to
6477 * perform */
6478#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006480 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006481 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006482 p_encoding = enc_skip(p_penc);
6483 if (*p_encoding == NUL)
6484 p_encoding = enc_skip(p_enc);
6485 if (prt_use_courier)
6486 {
6487 /* Include ASCII range encoding vector */
6488 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
6489 {
6490 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006491 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006492 return FALSE;
6493 }
6494 if (!prt_open_resource(&res_encoding))
6495 return FALSE;
6496 /* For the moment there are no checks on encoding resource files to
6497 * perform */
6498 }
6499 }
6500
6501 prt_conv.vc_type = CONV_NONE;
6502 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
6503 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006504 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
6505 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006506 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006507 p_encoding);
6508 return FALSE;
6509 }
6510 prt_do_conv = TRUE;
6511 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006512 prt_do_conv = prt_conv.vc_type != CONV_NONE;
6513
6514 if (prt_out_mbyte && prt_custom_cmap)
6515 {
6516 /* Find user supplied CMap */
6517 if (!prt_find_resource(prt_cmap, &res_cmap))
6518 {
6519 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006520 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006521 return FALSE;
6522 }
6523 if (!prt_open_resource(&res_cmap))
6524 return FALSE;
6525 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526#endif
6527
6528 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006529 STRCPY(buffer, res_prolog.title);
6530 STRCAT(buffer, " ");
6531 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006532 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
6533#ifdef FEAT_MBYTE
6534 if (prt_out_mbyte)
6535 {
6536 STRCPY(buffer, res_cidfont.title);
6537 STRCAT(buffer, " ");
6538 STRCAT(buffer, res_cidfont.version);
6539 prt_dsc_resources(NULL, "procset", buffer);
6540
6541 if (prt_custom_cmap)
6542 {
6543 STRCPY(buffer, res_cmap.title);
6544 STRCAT(buffer, " ");
6545 STRCAT(buffer, res_cmap.version);
6546 prt_dsc_resources(NULL, "cmap", buffer);
6547 }
6548 }
6549 if (!prt_out_mbyte || prt_use_courier)
6550#endif
6551 {
6552 STRCPY(buffer, res_encoding.title);
6553 STRCAT(buffer, " ");
6554 STRCAT(buffer, res_encoding.version);
6555 prt_dsc_resources(NULL, "encoding", buffer);
6556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
6558#ifdef FEAT_SYN_HL
6559 psettings->do_syntax
6560#else
6561 0
6562#endif
6563 , prt_num_copies);
6564 prt_dsc_noarg("EndComments");
6565
6566 /*
6567 * PS Document page defaults
6568 */
6569 prt_dsc_noarg("BeginDefaults");
6570
6571 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006572#ifdef FEAT_MBYTE
6573 if (!prt_out_mbyte || prt_use_courier)
6574#endif
6575 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
6576#ifdef FEAT_MBYTE
6577 if (prt_out_mbyte)
6578 {
6579 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
6580 &prt_ps_mb_font);
6581 if (!prt_custom_cmap)
6582 prt_dsc_resources(NULL, "cmap", prt_cmap);
6583 }
6584#endif
6585
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586 /* Paper will be used for all pages */
6587 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
6588
6589 prt_dsc_noarg("EndDefaults");
6590
6591 /*
6592 * PS Document prolog inclusion - all required procsets.
6593 */
6594 prt_dsc_noarg("BeginProlog");
6595
Bram Moolenaar8299df92004-07-10 09:47:34 +00006596 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597 if (!prt_add_resource(&res_prolog))
6598 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006599#ifdef FEAT_MBYTE
6600 if (prt_out_mbyte)
6601 {
6602 /* Add CID font procset, and any user supplied CMap */
6603 if (!prt_add_resource(&res_cidfont))
6604 return FALSE;
6605 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
6606 return FALSE;
6607 }
6608#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006609
Bram Moolenaar8299df92004-07-10 09:47:34 +00006610#ifdef FEAT_MBYTE
6611 if (!prt_out_mbyte || prt_use_courier)
6612#endif
6613 /* There will be only one Roman font encoding to be included in the PS
6614 * file. */
6615 if (!prt_add_resource(&res_encoding))
6616 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006617
6618 prt_dsc_noarg("EndProlog");
6619
6620 /*
6621 * PS Document setup - must appear after the prolog
6622 */
6623 prt_dsc_noarg("BeginSetup");
6624
6625 /* Device setup - page size and number of uncollated copies */
6626 prt_write_int((int)prt_mediasize[prt_media].width);
6627 prt_write_int((int)prt_mediasize[prt_media].height);
6628 prt_write_int(0);
6629 prt_write_string("sps\n");
6630 prt_write_int(prt_num_copies);
6631 prt_write_string("nc\n");
6632 prt_write_boolean(prt_duplex);
6633 prt_write_boolean(prt_tumble);
6634 prt_write_string("dt\n");
6635 prt_write_boolean(prt_collate);
6636 prt_write_string("c\n");
6637
6638 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006639#ifdef FEAT_MBYTE
6640 if (!prt_out_mbyte || prt_use_courier)
6641 {
6642 /* When using Courier for ASCII range when printing multi-byte, need to
6643 * pick up ASCII encoding to use with it. */
6644 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006645 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006646#endif
6647 prt_dsc_resources("IncludeResource", "font",
6648 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6649 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
6650 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6651 prt_dsc_resources("IncludeResource", "font",
6652 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6653 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
6654 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6655 prt_dsc_resources("IncludeResource", "font",
6656 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6657 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
6658 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6659 prt_dsc_resources("IncludeResource", "font",
6660 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6661 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
6662 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6663#ifdef FEAT_MBYTE
6664 }
6665 if (prt_out_mbyte)
6666 {
6667 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
6668 * not have an italic form being a western style, so where no font is
6669 * defined for these faces VIM falls back to an existing face.
6670 * Note: if using Courier for the ASCII range then the printout will
6671 * have bold/italic/bolditalic regardless of the setting of printmbfont.
6672 */
6673 prt_dsc_resources("IncludeResource", "font",
6674 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6675 if (!prt_custom_cmap)
6676 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6677 prt_def_cidfont("CF0", (int)prt_line_height,
6678 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6679
6680 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
6681 {
6682 prt_dsc_resources("IncludeResource", "font",
6683 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6684 if (!prt_custom_cmap)
6685 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6686 prt_def_cidfont("CF1", (int)prt_line_height,
6687 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6688 }
6689 else
6690 /* Use ROMAN for BOLD */
6691 prt_dup_cidfont("CF0", "CF1");
6692
6693 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
6694 {
6695 prt_dsc_resources("IncludeResource", "font",
6696 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6697 if (!prt_custom_cmap)
6698 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6699 prt_def_cidfont("CF2", (int)prt_line_height,
6700 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6701 }
6702 else
6703 /* Use ROMAN for OBLIQUE */
6704 prt_dup_cidfont("CF0", "CF2");
6705
6706 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
6707 {
6708 prt_dsc_resources("IncludeResource", "font",
6709 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6710 if (!prt_custom_cmap)
6711 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6712 prt_def_cidfont("CF3", (int)prt_line_height,
6713 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6714 }
6715 else
6716 /* Use BOLD for BOLDOBLIQUE */
6717 prt_dup_cidfont("CF1", "CF3");
6718 }
6719#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006720
6721 /* Misc constant vars used for underlining and background rects */
6722 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006723 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006725 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006726 prt_def_var("BO", prt_bgcol_offset, 2);
6727
6728 prt_dsc_noarg("EndSetup");
6729
6730 /* Fail if any problems writing out to the PS file */
6731 return !prt_file_error;
6732}
6733
6734 void
6735mch_print_end(psettings)
6736 prt_settings_T *psettings;
6737{
6738 prt_dsc_noarg("Trailer");
6739
6740 /*
6741 * Output any info we don't know in toto until we finish
6742 */
6743 prt_dsc_ints("Pages", 1, &prt_page_num);
6744
6745 prt_dsc_noarg("EOF");
6746
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006747 /* Write CTRL-D to close serial communication link if used.
6748 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006749 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006750
Bram Moolenaar071d4272004-06-13 20:20:40 +00006751 if (!prt_file_error && psettings->outfile == NULL
6752 && !got_int && !psettings->user_abort)
6753 {
6754 /* Close the file first. */
6755 if (prt_ps_fd != NULL)
6756 {
6757 fclose(prt_ps_fd);
6758 prt_ps_fd = NULL;
6759 }
6760 prt_message((char_u *)_("Sending to printer..."));
6761
6762 /* Not printing to a file: use 'printexpr' to print the file. */
6763 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6764 EMSG(_("E365: Failed to print PostScript file"));
6765 else
6766 prt_message((char_u *)_("Print job sent."));
6767 }
6768
6769 mch_print_cleanup();
6770}
6771
6772 int
6773mch_print_end_page()
6774{
6775 prt_flush_buffer();
6776
6777 prt_write_string("re sp\n");
6778
6779 prt_dsc_noarg("PageTrailer");
6780
6781 return !prt_file_error;
6782}
6783
6784/*ARGSUSED*/
6785 int
6786mch_print_begin_page(str)
6787 char_u *str;
6788{
6789 int page_num[2];
6790
6791 prt_page_num++;
6792
6793 page_num[0] = page_num[1] = prt_page_num;
6794 prt_dsc_ints("Page", 2, page_num);
6795
6796 prt_dsc_noarg("BeginPageSetup");
6797
Bram Moolenaar8299df92004-07-10 09:47:34 +00006798 prt_write_string("sv\n0 g\n");
6799#ifdef FEAT_MBYTE
6800 prt_in_ascii = !prt_out_mbyte;
6801 if (prt_out_mbyte)
6802 prt_write_string("CF0 sf\n");
6803 else
6804#endif
6805 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806 prt_fgcol = PRCOLOR_BLACK;
6807 prt_bgcol = PRCOLOR_WHITE;
6808 prt_font = PRT_PS_FONT_ROMAN;
6809
6810 /* Set up page transformation for landscape printing. */
6811 if (!prt_portrait)
6812 {
6813 prt_write_int(-((int)prt_mediasize[prt_media].width));
6814 prt_write_string("sl\n");
6815 }
6816
6817 prt_dsc_noarg("EndPageSetup");
6818
6819 /* We have reset the font attributes, force setting them again. */
6820 curr_bg = (long_u)0xffffffff;
6821 curr_fg = (long_u)0xffffffff;
6822 curr_bold = MAYBE;
6823
6824 return !prt_file_error;
6825}
6826
6827 int
6828mch_print_blank_page()
6829{
6830 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6831}
6832
6833static float prt_pos_x = 0;
6834static float prt_pos_y = 0;
6835
6836 void
6837mch_print_start_line(margin, page_line)
6838 int margin;
6839 int page_line;
6840{
6841 prt_pos_x = prt_left_margin;
6842 if (margin)
6843 prt_pos_x -= prt_number_width;
6844
6845 prt_pos_y = prt_top_margin - prt_first_line_height -
6846 page_line * prt_line_height;
6847
6848 prt_attribute_change = TRUE;
6849 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006850#ifdef FEAT_MBYTE
6851 prt_half_width = FALSE;
6852#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853}
6854
6855/*ARGSUSED*/
6856 int
6857mch_print_text_out(p, len)
6858 char_u *p;
6859 int len;
6860{
6861 int need_break;
6862 char_u ch;
6863 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006864 float char_width;
6865 float next_pos;
6866#ifdef FEAT_MBYTE
6867 int in_ascii;
6868 int half_width;
6869#endif
6870
6871 char_width = prt_char_width;
6872
6873#ifdef FEAT_MBYTE
6874 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6875 * CJKV font to do what VIM is doing here - use a Roman font for characters
6876 * in the ASCII range, and the origingal CID font for everything else.
6877 * The problem is that GhostScript still (as of 8.13) does not support
6878 * rearranged fonts even though they have been documented by Adobe for 7
6879 * years! If they ever do, a lot of this code will disappear.
6880 */
6881 if (prt_use_courier)
6882 {
6883 in_ascii = (len == 1 && *p < 0x80);
6884 if (prt_in_ascii)
6885 {
6886 if (!in_ascii)
6887 {
6888 /* No longer in ASCII range - need to switch font */
6889 prt_in_ascii = FALSE;
6890 prt_need_font = TRUE;
6891 prt_attribute_change = TRUE;
6892 }
6893 }
6894 else if (in_ascii)
6895 {
6896 /* Now in ASCII range - need to switch font */
6897 prt_in_ascii = TRUE;
6898 prt_need_font = TRUE;
6899 prt_attribute_change = TRUE;
6900 }
6901 }
6902 if (prt_out_mbyte)
6903 {
6904 half_width = ((*mb_ptr2cells)(p) == 1);
6905 if (half_width)
6906 char_width /= 2;
6907 if (prt_half_width)
6908 {
6909 if (!half_width)
6910 {
6911 prt_half_width = FALSE;
6912 prt_pos_x += prt_char_width/4;
6913 prt_need_moveto = TRUE;
6914 prt_attribute_change = TRUE;
6915 }
6916 }
6917 else if (half_width)
6918 {
6919 prt_half_width = TRUE;
6920 prt_pos_x += prt_char_width/4;
6921 prt_need_moveto = TRUE;
6922 prt_attribute_change = TRUE;
6923 }
6924 }
6925#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006926
6927 /* Output any required changes to the graphics state, after flushing any
6928 * text buffered so far.
6929 */
6930 if (prt_attribute_change)
6931 {
6932 prt_flush_buffer();
6933 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006934 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006935
6936 if (prt_need_moveto)
6937 {
6938 prt_pos_x_moveto = prt_pos_x;
6939 prt_pos_y_moveto = prt_pos_y;
6940 prt_do_moveto = TRUE;
6941
6942 prt_need_moveto = FALSE;
6943 }
6944 if (prt_need_font)
6945 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006946#ifdef FEAT_MBYTE
6947 if (!prt_in_ascii)
6948 prt_write_string("CF");
6949 else
6950#endif
6951 prt_write_string("F");
6952 prt_write_int(prt_font);
6953 prt_write_string("sf\n");
6954 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006955 }
6956 if (prt_need_fgcol)
6957 {
6958 int r, g, b;
6959 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6960 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6961 b = prt_fgcol & 0xff;
6962
6963 prt_write_real(r / 255.0, 3);
6964 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006965 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006966 else
6967 {
6968 prt_write_real(g / 255.0, 3);
6969 prt_write_real(b / 255.0, 3);
6970 prt_write_string("r\n");
6971 }
6972 prt_need_fgcol = FALSE;
6973 }
6974
6975 if (prt_bgcol != PRCOLOR_WHITE)
6976 {
6977 prt_new_bgcol = prt_bgcol;
6978 if (prt_need_bgcol)
6979 prt_do_bgcol = TRUE;
6980 }
6981 else
6982 prt_do_bgcol = FALSE;
6983 prt_need_bgcol = FALSE;
6984
6985 if (prt_need_underline)
6986 prt_do_underline = prt_underline;
6987 prt_need_underline = FALSE;
6988
6989 prt_attribute_change = FALSE;
6990 }
6991
6992#ifdef FEAT_MBYTE
6993 if (prt_do_conv)
6994 {
6995 /* Convert from multi-byte to 8-bit encoding */
6996 p = string_convert(&prt_conv, p, &len);
6997 if (p == NULL)
6998 p = (char_u *)"";
6999 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007000
Bram Moolenaar8299df92004-07-10 09:47:34 +00007001 if (prt_out_mbyte)
7002 {
7003 /* Multi-byte character strings are represented more efficiently as hex
7004 * strings when outputting clean 8 bit PS.
7005 */
7006 do
7007 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00007008 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00007009 ga_append(&prt_ps_buffer, ch);
7010 ch = prt_hexchar[(*p) & 0xf];
7011 ga_append(&prt_ps_buffer, ch);
7012 p++;
7013 }
7014 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007015 }
7016 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00007017#endif
7018 {
7019 /* Add next character to buffer of characters to output.
7020 * Note: One printed character may require several PS characters to
7021 * represent it, but we only count them as one printed character.
7022 */
7023 ch = *p;
7024 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
7025 {
7026 /* Convert non-printing characters to either their escape or octal
7027 * sequence, ensures PS sent over a serial line does not interfere
7028 * with the comms protocol. Note: For EBCDIC we need to write out
7029 * the escape sequences as ASCII codes!
7030 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
7031 */
7032 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
7033 switch (ch)
7034 {
7035 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
7036 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
7037 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
7038 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
7039 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
7040 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
7041 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
7042 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
7043
7044 default:
7045 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
7046#ifdef EBCDIC
7047 ebcdic2ascii(ch_buff, 3);
7048#endif
7049 ga_append(&prt_ps_buffer, ch_buff[0]);
7050 ga_append(&prt_ps_buffer, ch_buff[1]);
7051 ga_append(&prt_ps_buffer, ch_buff[2]);
7052 break;
7053 }
7054 }
7055 else
7056 ga_append(&prt_ps_buffer, ch);
7057 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007058
7059#ifdef FEAT_MBYTE
7060 /* Need to free any translated characters */
7061 if (prt_do_conv && (*p != NUL))
7062 vim_free(p);
7063#endif
7064
Bram Moolenaar8299df92004-07-10 09:47:34 +00007065 prt_text_run += char_width;
7066 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007067
Bram Moolenaar8299df92004-07-10 09:47:34 +00007068 /* The downside of fp - use relative error on right margin check */
7069 next_pos = prt_pos_x + prt_char_width;
7070 need_break = (next_pos > prt_right_margin) &&
7071 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007072
7073 if (need_break)
7074 prt_flush_buffer();
7075
7076 return need_break;
7077}
7078
7079 void
7080mch_print_set_font(iBold, iItalic, iUnderline)
7081 int iBold;
7082 int iItalic;
7083 int iUnderline;
7084{
7085 int font = 0;
7086
7087 if (iBold)
7088 font |= 0x01;
7089 if (iItalic)
7090 font |= 0x02;
7091
7092 if (font != prt_font)
7093 {
7094 prt_font = font;
7095 prt_attribute_change = TRUE;
7096 prt_need_font = TRUE;
7097 }
7098 if (prt_underline != iUnderline)
7099 {
7100 prt_underline = iUnderline;
7101 prt_attribute_change = TRUE;
7102 prt_need_underline = TRUE;
7103 }
7104}
7105
7106 void
7107mch_print_set_bg(bgcol)
7108 long_u bgcol;
7109{
7110 prt_bgcol = bgcol;
7111 prt_attribute_change = TRUE;
7112 prt_need_bgcol = TRUE;
7113}
7114
7115 void
7116mch_print_set_fg(fgcol)
7117 long_u fgcol;
7118{
7119 if (fgcol != (long_u)prt_fgcol)
7120 {
7121 prt_fgcol = fgcol;
7122 prt_attribute_change = TRUE;
7123 prt_need_fgcol = TRUE;
7124 }
7125}
7126
7127# endif /*FEAT_POSTSCRIPT*/
7128#endif /*FEAT_PRINTER*/
7129
7130#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7131 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
7132static char *get_locale_val __ARGS((int what));
7133
7134 static char *
7135get_locale_val(what)
7136 int what;
7137{
7138 char *loc;
7139
7140 /* Obtain the locale value from the libraries. For DJGPP this is
7141 * redefined and it doesn't use the arguments. */
7142 loc = setlocale(what, NULL);
7143
7144# if defined(__BORLANDC__)
7145 if (loc != NULL)
7146 {
7147 char_u *p;
7148
7149 /* Borland returns something like "LC_CTYPE=<name>\n"
7150 * Let's try to fix that bug here... */
7151 p = vim_strchr(loc, '=');
7152 if (p != NULL)
7153 {
7154 loc = ++p;
7155 while (*p != NUL) /* remove trailing newline */
7156 {
7157 if (*p < ' ')
7158 {
7159 *p = NUL;
7160 break;
7161 }
7162 ++p;
7163 }
7164 }
7165 }
7166# endif
7167
7168 return loc;
7169}
7170#endif
7171
7172
7173#ifdef WIN32
7174/*
7175 * On MS-Windows locale names are strings like "German_Germany.1252", but
7176 * gettext expects "de". Try to translate one into another here for a few
7177 * supported languages.
7178 */
7179 static char_u *
7180gettext_lang(char_u *name)
7181{
7182 int i;
7183 static char *(mtable[]) = {
7184 "afrikaans", "af",
7185 "czech", "cs",
7186 "dutch", "nl",
7187 "german", "de",
7188 "english_united kingdom", "en_GB",
7189 "spanish", "es",
7190 "french", "fr",
7191 "italian", "it",
7192 "japanese", "ja",
7193 "korean", "ko",
7194 "norwegian", "no",
7195 "polish", "pl",
7196 "russian", "ru",
7197 "slovak", "sk",
7198 "swedish", "sv",
7199 "ukrainian", "uk",
7200 "chinese_china", "zh_CN",
7201 "chinese_taiwan", "zh_TW",
7202 NULL};
7203
7204 for (i = 0; mtable[i] != NULL; i += 2)
7205 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
7206 return mtable[i + 1];
7207 return name;
7208}
7209#endif
7210
7211#if defined(FEAT_MULTI_LANG) || defined(PROTO)
7212/*
7213 * Obtain the current messages language. Used to set the default for
7214 * 'helplang'. May return NULL or an empty string.
7215 */
7216 char_u *
7217get_mess_lang()
7218{
7219 char_u *p;
7220
7221# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
7222# if defined(LC_MESSAGES)
7223 p = (char_u *)get_locale_val(LC_MESSAGES);
7224# else
7225 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
7226 * may be set to the LCID number. */
7227 p = (char_u *)get_locale_val(LC_ALL);
7228# endif
7229# else
7230 p = mch_getenv((char_u *)"LC_ALL");
7231 if (p == NULL || *p == NUL)
7232 {
7233 p = mch_getenv((char_u *)"LC_MESSAGES");
7234 if (p == NULL || *p == NUL)
7235 p = mch_getenv((char_u *)"LANG");
7236 }
7237# endif
7238# ifdef WIN32
7239 p = gettext_lang(p);
7240# endif
7241 return p;
7242}
7243#endif
7244
Bram Moolenaardef9e822004-12-31 20:58:58 +00007245/* Complicated #if; matches with where get_mess_env() is used below. */
7246#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7247 && defined(LC_MESSAGES))) \
7248 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7249 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
7250 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007251static char_u *get_mess_env __ARGS((void));
7252
7253/*
7254 * Get the language used for messages from the environment.
7255 */
7256 static char_u *
7257get_mess_env()
7258{
7259 char_u *p;
7260
7261 p = mch_getenv((char_u *)"LC_ALL");
7262 if (p == NULL || *p == NUL)
7263 {
7264 p = mch_getenv((char_u *)"LC_MESSAGES");
7265 if (p == NULL || *p == NUL)
7266 {
7267 p = mch_getenv((char_u *)"LANG");
7268 if (p != NULL && VIM_ISDIGIT(*p))
7269 p = NULL; /* ignore something like "1043" */
7270# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7271 if (p == NULL || *p == NUL)
7272 p = (char_u *)get_locale_val(LC_CTYPE);
7273# endif
7274 }
7275 }
7276 return p;
7277}
7278#endif
7279
7280#if defined(FEAT_EVAL) || defined(PROTO)
7281
7282/*
7283 * Set the "v:lang" variable according to the current locale setting.
7284 * Also do "v:lc_time"and "v:ctype".
7285 */
7286 void
7287set_lang_var()
7288{
7289 char_u *loc;
7290
7291# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7292 loc = (char_u *)get_locale_val(LC_CTYPE);
7293# else
7294 /* setlocale() not supported: use the default value */
7295 loc = (char_u *)"C";
7296# endif
7297 set_vim_var_string(VV_CTYPE, loc, -1);
7298
7299 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
7300 * back to LC_CTYPE if it's empty. */
7301# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
7302 loc = (char_u *)get_locale_val(LC_MESSAGES);
7303# else
7304 loc = get_mess_env();
7305# endif
7306 set_vim_var_string(VV_LANG, loc, -1);
7307
7308# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7309 loc = (char_u *)get_locale_val(LC_TIME);
7310# endif
7311 set_vim_var_string(VV_LC_TIME, loc, -1);
7312}
7313#endif
7314
7315#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7316 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
7317/*
7318 * ":language": Set the language (locale).
7319 */
7320 void
7321ex_language(eap)
7322 exarg_T *eap;
7323{
7324 char *loc;
7325 char_u *p;
7326 char_u *name;
7327 int what = LC_ALL;
7328 char *whatstr = "";
7329#ifdef LC_MESSAGES
7330# define VIM_LC_MESSAGES LC_MESSAGES
7331#else
7332# define VIM_LC_MESSAGES 6789
7333#endif
7334
7335 name = eap->arg;
7336
7337 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
7338 * Allow abbreviation, but require at least 3 characters to avoid
7339 * confusion with a two letter language name "me" or "ct". */
7340 p = skiptowhite(eap->arg);
7341 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
7342 {
7343 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
7344 {
7345 what = VIM_LC_MESSAGES;
7346 name = skipwhite(p);
7347 whatstr = "messages ";
7348 }
7349 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
7350 {
7351 what = LC_CTYPE;
7352 name = skipwhite(p);
7353 whatstr = "ctype ";
7354 }
7355 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
7356 {
7357 what = LC_TIME;
7358 name = skipwhite(p);
7359 whatstr = "time ";
7360 }
7361 }
7362
7363 if (*name == NUL)
7364 {
7365#ifndef LC_MESSAGES
7366 if (what == VIM_LC_MESSAGES)
7367 p = get_mess_env();
7368 else
7369#endif
7370 p = (char_u *)setlocale(what, NULL);
7371 if (p == NULL || *p == NUL)
7372 p = (char_u *)"Unknown";
7373 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
7374 }
7375 else
7376 {
7377#ifndef LC_MESSAGES
7378 if (what == VIM_LC_MESSAGES)
7379 loc = "";
7380 else
7381#endif
7382 loc = setlocale(what, (char *)name);
7383 if (loc == NULL)
7384 EMSG2(_("E197: Cannot set language to \"%s\""), name);
7385 else
7386 {
7387#ifdef HAVE_NL_MSG_CAT_CNTR
7388 /* Need to do this for GNU gettext, otherwise cached translations
7389 * will be used again. */
7390 extern int _nl_msg_cat_cntr;
7391
7392 ++_nl_msg_cat_cntr;
7393#endif
7394 /* Reset $LC_ALL, otherwise it would overrule everyting. */
7395 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
7396
7397 if (what != LC_TIME)
7398 {
7399 /* Tell gettext() what to translate to. It apparently doesn't
7400 * use the currently effective locale. Also do this when
7401 * FEAT_GETTEXT isn't defined, so that shell commands use this
7402 * value. */
7403 if (what == LC_ALL)
7404 vim_setenv((char_u *)"LANG", name);
7405 if (what != LC_CTYPE)
7406 {
7407 char_u *mname;
7408#ifdef WIN32
7409 mname = gettext_lang(name);
7410#else
7411 mname = name;
7412#endif
7413 vim_setenv((char_u *)"LC_MESSAGES", mname);
7414#ifdef FEAT_MULTI_LANG
7415 set_helplang_default(mname);
7416#endif
7417 }
7418
7419 /* Set $LC_CTYPE, because it overrules $LANG, and
7420 * gtk_set_locale() calls setlocale() again. gnome_init()
7421 * sets $LC_CTYPE to "en_US" (that's a bug!). */
7422 if (what != VIM_LC_MESSAGES)
7423 vim_setenv((char_u *)"LC_CTYPE", name);
7424# ifdef FEAT_GUI_GTK
7425 /* Let GTK know what locale we're using. Not sure this is
7426 * really needed... */
7427 if (gui.in_use)
7428 (void)gtk_set_locale();
7429# endif
7430 }
7431
7432# ifdef FEAT_EVAL
7433 /* Set v:lang, v:lc_time and v:ctype to the final result. */
7434 set_lang_var();
7435# endif
7436 }
7437 }
7438}
7439
7440# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
7441/*
7442 * Function given to ExpandGeneric() to obtain the possible arguments of the
7443 * ":language" command.
7444 */
7445/*ARGSUSED*/
7446 char_u *
7447get_lang_arg(xp, idx)
7448 expand_T *xp;
7449 int idx;
7450{
7451 if (idx == 0)
7452 return (char_u *)"messages";
7453 if (idx == 1)
7454 return (char_u *)"ctype";
7455 if (idx == 2)
7456 return (char_u *)"time";
7457 return NULL;
7458}
7459# endif
7460
7461#endif