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