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