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