Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1 | /* vi:set ts=8 sts=4 sw=4 noet: |
| 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 | * session.c: session related functions |
| 12 | */ |
| 13 | |
| 14 | #include "vim.h" |
| 15 | |
| 16 | #if defined(FEAT_SESSION) || defined(PROTO) |
| 17 | |
| 18 | static int did_lcd; // whether ":lcd" was produced for a session |
| 19 | |
| 20 | /* |
| 21 | * Write a file name to the session file. |
| 22 | * Takes care of the "slash" option in 'sessionoptions' and escapes special |
| 23 | * characters. |
| 24 | * Returns FAIL if writing fails or out of memory. |
| 25 | */ |
| 26 | static int |
| 27 | ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) |
| 28 | { |
| 29 | char_u *sname; |
| 30 | char_u *p; |
| 31 | int retval = OK; |
| 32 | |
| 33 | sname = home_replace_save(NULL, name); |
| 34 | if (sname == NULL) |
| 35 | return FAIL; |
| 36 | |
| 37 | if (*flagp & SSOP_SLASH) |
| 38 | { |
| 39 | // change all backslashes to forward slashes |
| 40 | for (p = sname; *p != NUL; MB_PTR_ADV(p)) |
| 41 | if (*p == '\\') |
| 42 | *p = '/'; |
| 43 | } |
| 44 | |
| 45 | // escape special characters |
| 46 | p = vim_strsave_fnameescape(sname, FALSE); |
| 47 | vim_free(sname); |
| 48 | if (p == NULL) |
| 49 | return FAIL; |
| 50 | |
| 51 | // write the result |
| 52 | if (fputs((char *)p, fd) < 0) |
| 53 | retval = FAIL; |
| 54 | |
| 55 | vim_free(p); |
| 56 | return retval; |
| 57 | } |
| 58 | |
| 59 | /* |
| 60 | * Write a buffer name to the session file. |
| 61 | * Also ends the line, if "add_eol" is TRUE. |
| 62 | * Returns FAIL if writing fails. |
| 63 | */ |
| 64 | static int |
| 65 | ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, int add_eol) |
| 66 | { |
| 67 | char_u *name; |
| 68 | |
| 69 | // Use the short file name if the current directory is known at the time |
| 70 | // the session file will be sourced. |
| 71 | // Don't do this for ":mkview", we don't know the current directory. |
| 72 | // Don't do this after ":lcd", we don't keep track of what the current |
| 73 | // directory is. |
| 74 | if (buf->b_sfname != NULL |
| 75 | && flagp == &ssop_flags |
| 76 | && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR)) |
| 77 | #ifdef FEAT_AUTOCHDIR |
| 78 | && !p_acd |
| 79 | #endif |
| 80 | && !did_lcd) |
| 81 | name = buf->b_sfname; |
| 82 | else |
| 83 | name = buf->b_ffname; |
| 84 | if (ses_put_fname(fd, name, flagp) == FAIL |
| 85 | || (add_eol && put_eol(fd) == FAIL)) |
| 86 | return FAIL; |
| 87 | return OK; |
| 88 | } |
| 89 | |
| 90 | /* |
| 91 | * Write an argument list to the session file. |
| 92 | * Returns FAIL if writing fails. |
| 93 | */ |
| 94 | static int |
| 95 | ses_arglist( |
| 96 | FILE *fd, |
| 97 | char *cmd, |
| 98 | garray_T *gap, |
| 99 | int fullname, // TRUE: use full path name |
| 100 | unsigned *flagp) |
| 101 | { |
| 102 | int i; |
| 103 | char_u *buf = NULL; |
| 104 | char_u *s; |
| 105 | |
| 106 | if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL) |
| 107 | return FAIL; |
| 108 | if (put_line(fd, "%argdel") == FAIL) |
| 109 | return FAIL; |
| 110 | for (i = 0; i < gap->ga_len; ++i) |
| 111 | { |
| 112 | // NULL file names are skipped (only happens when out of memory). |
| 113 | s = alist_name(&((aentry_T *)gap->ga_data)[i]); |
| 114 | if (s != NULL) |
| 115 | { |
| 116 | if (fullname) |
| 117 | { |
| 118 | buf = alloc(MAXPATHL); |
| 119 | if (buf != NULL) |
| 120 | { |
| 121 | (void)vim_FullName(s, buf, MAXPATHL, FALSE); |
| 122 | s = buf; |
| 123 | } |
| 124 | } |
| 125 | if (fputs("$argadd ", fd) < 0 |
| 126 | || ses_put_fname(fd, s, flagp) == FAIL |
| 127 | || put_eol(fd) == FAIL) |
| 128 | { |
| 129 | vim_free(buf); |
| 130 | return FAIL; |
| 131 | } |
| 132 | vim_free(buf); |
| 133 | } |
| 134 | } |
| 135 | return OK; |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | * Return non-zero if window "wp" is to be stored in the Session. |
| 140 | */ |
| 141 | static int |
| 142 | ses_do_win(win_T *wp) |
| 143 | { |
| 144 | #ifdef FEAT_TERMINAL |
| 145 | if (bt_terminal(wp->w_buffer)) |
| 146 | return !term_is_finished(wp->w_buffer) |
| 147 | && (ssop_flags & SSOP_TERMINAL) |
| 148 | && term_should_restore(wp->w_buffer); |
| 149 | #endif |
| 150 | if (wp->w_buffer->b_fname == NULL |
| 151 | #ifdef FEAT_QUICKFIX |
| 152 | // When 'buftype' is "nofile" can't restore the window contents. |
| 153 | || bt_nofilename(wp->w_buffer) |
| 154 | #endif |
| 155 | ) |
| 156 | return (ssop_flags & SSOP_BLANK); |
| 157 | if (bt_help(wp->w_buffer)) |
| 158 | return (ssop_flags & SSOP_HELP); |
| 159 | return TRUE; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * Return TRUE if frame "fr" has a window somewhere that we want to save in |
| 164 | * the Session. |
| 165 | */ |
| 166 | static int |
| 167 | ses_do_frame(frame_T *fr) |
| 168 | { |
| 169 | frame_T *frc; |
| 170 | |
| 171 | if (fr->fr_layout == FR_LEAF) |
| 172 | return ses_do_win(fr->fr_win); |
| 173 | FOR_ALL_FRAMES(frc, fr->fr_child) |
| 174 | if (ses_do_frame(frc)) |
| 175 | return TRUE; |
| 176 | return FALSE; |
| 177 | } |
| 178 | |
| 179 | /* |
| 180 | * Skip frames that don't contain windows we want to save in the Session. |
| 181 | * Returns NULL when there none. |
| 182 | */ |
| 183 | static frame_T * |
| 184 | ses_skipframe(frame_T *fr) |
| 185 | { |
| 186 | frame_T *frc; |
| 187 | |
| 188 | FOR_ALL_FRAMES(frc, fr) |
| 189 | if (ses_do_frame(frc)) |
| 190 | break; |
| 191 | return frc; |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | * Write commands to "fd" to recursively create windows for frame "fr", |
| 196 | * horizontally and vertically split. |
| 197 | * After the commands the last window in the frame is the current window. |
| 198 | * Returns FAIL when writing the commands to "fd" fails. |
| 199 | */ |
| 200 | static int |
| 201 | ses_win_rec(FILE *fd, frame_T *fr) |
| 202 | { |
| 203 | frame_T *frc; |
| 204 | int count = 0; |
| 205 | |
| 206 | if (fr->fr_layout != FR_LEAF) |
| 207 | { |
| 208 | // Find first frame that's not skipped and then create a window for |
| 209 | // each following one (first frame is already there). |
| 210 | frc = ses_skipframe(fr->fr_child); |
| 211 | if (frc != NULL) |
| 212 | while ((frc = ses_skipframe(frc->fr_next)) != NULL) |
| 213 | { |
| 214 | // Make window as big as possible so that we have lots of room |
| 215 | // to split. |
| 216 | if (put_line(fd, "wincmd _ | wincmd |") == FAIL |
| 217 | || put_line(fd, fr->fr_layout == FR_COL |
| 218 | ? "split" : "vsplit") == FAIL) |
| 219 | return FAIL; |
| 220 | ++count; |
| 221 | } |
| 222 | |
| 223 | // Go back to the first window. |
| 224 | if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL |
| 225 | ? "%dwincmd k" : "%dwincmd h", count) < 0 |
| 226 | || put_eol(fd) == FAIL)) |
| 227 | return FAIL; |
| 228 | |
| 229 | // Recursively create frames/windows in each window of this column or |
| 230 | // row. |
| 231 | frc = ses_skipframe(fr->fr_child); |
| 232 | while (frc != NULL) |
| 233 | { |
| 234 | ses_win_rec(fd, frc); |
| 235 | frc = ses_skipframe(frc->fr_next); |
| 236 | // Go to next window. |
| 237 | if (frc != NULL && put_line(fd, "wincmd w") == FAIL) |
| 238 | return FAIL; |
| 239 | } |
| 240 | } |
| 241 | return OK; |
| 242 | } |
| 243 | |
| 244 | static int |
| 245 | ses_winsizes( |
| 246 | FILE *fd, |
| 247 | int restore_size, |
| 248 | win_T *tab_firstwin) |
| 249 | { |
| 250 | int n = 0; |
| 251 | win_T *wp; |
| 252 | |
| 253 | if (restore_size && (ssop_flags & SSOP_WINSIZE)) |
| 254 | { |
| 255 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 256 | { |
| 257 | if (!ses_do_win(wp)) |
| 258 | continue; |
| 259 | ++n; |
| 260 | |
| 261 | // restore height when not full height |
| 262 | if (wp->w_height + wp->w_status_height < topframe->fr_height |
| 263 | && (fprintf(fd, |
| 264 | "exe '%dresize ' . ((&lines * %ld + %ld) / %ld)", |
| 265 | n, (long)wp->w_height, Rows / 2, Rows) < 0 |
| 266 | || put_eol(fd) == FAIL)) |
| 267 | return FAIL; |
| 268 | |
| 269 | // restore width when not full width |
| 270 | if (wp->w_width < Columns && (fprintf(fd, |
| 271 | "exe 'vert %dresize ' . ((&columns * %ld + %ld) / %ld)", |
| 272 | n, (long)wp->w_width, Columns / 2, Columns) < 0 |
| 273 | || put_eol(fd) == FAIL)) |
| 274 | return FAIL; |
| 275 | } |
| 276 | } |
| 277 | else |
| 278 | { |
| 279 | // Just equalise window sizes |
| 280 | if (put_line(fd, "wincmd =") == FAIL) |
| 281 | return FAIL; |
| 282 | } |
| 283 | return OK; |
| 284 | } |
| 285 | |
| 286 | static int |
| 287 | put_view_curpos(FILE *fd, win_T *wp, char *spaces) |
| 288 | { |
| 289 | int r; |
| 290 | |
| 291 | if (wp->w_curswant == MAXCOL) |
| 292 | r = fprintf(fd, "%snormal! $", spaces); |
| 293 | else |
| 294 | r = fprintf(fd, "%snormal! 0%d|", spaces, wp->w_virtcol + 1); |
| 295 | return r < 0 || put_eol(fd) == FAIL ? FALSE : OK; |
| 296 | } |
| 297 | |
| 298 | /* |
| 299 | * Write commands to "fd" to restore the view of a window. |
| 300 | * Caller must make sure 'scrolloff' is zero. |
| 301 | */ |
| 302 | static int |
| 303 | put_view( |
| 304 | FILE *fd, |
| 305 | win_T *wp, |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 306 | int add_edit, // add ":edit" command to view |
| 307 | unsigned *flagp, // vop_flags or ssop_flags |
| 308 | int current_arg_idx, // current argument index of the window, |
| 309 | // use -1 if unknown |
| 310 | hashtab_T *terminal_bufs UNUSED) // already encountered terminal buffers, |
| 311 | // can be NULL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 312 | { |
| 313 | win_T *save_curwin; |
| 314 | int f; |
| 315 | int do_cursor; |
| 316 | int did_next = FALSE; |
| 317 | |
| 318 | // Always restore cursor position for ":mksession". For ":mkview" only |
| 319 | // when 'viewoptions' contains "cursor". |
| 320 | do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR); |
| 321 | |
| 322 | // Local argument list. |
| 323 | if (wp->w_alist == &global_alist) |
| 324 | { |
| 325 | if (put_line(fd, "argglobal") == FAIL) |
| 326 | return FAIL; |
| 327 | } |
| 328 | else |
| 329 | { |
| 330 | if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga, |
| 331 | flagp == &vop_flags |
| 332 | || !(*flagp & SSOP_CURDIR) |
| 333 | || wp->w_localdir != NULL, flagp) == FAIL) |
| 334 | return FAIL; |
| 335 | } |
| 336 | |
| 337 | // Only when part of a session: restore the argument index. Some |
| 338 | // arguments may have been deleted, check if the index is valid. |
| 339 | if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp) |
| 340 | && flagp == &ssop_flags) |
| 341 | { |
| 342 | if (fprintf(fd, "%ldargu", (long)wp->w_arg_idx + 1) < 0 |
| 343 | || put_eol(fd) == FAIL) |
| 344 | return FAIL; |
| 345 | did_next = TRUE; |
| 346 | } |
| 347 | |
| 348 | // Edit the file. Skip this when ":next" already did it. |
| 349 | if (add_edit && (!did_next || wp->w_arg_idx_invalid)) |
| 350 | { |
| 351 | # ifdef FEAT_TERMINAL |
| 352 | if (bt_terminal(wp->w_buffer)) |
| 353 | { |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 354 | if (term_write_session(fd, wp, terminal_bufs) == FAIL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 355 | return FAIL; |
| 356 | } |
| 357 | else |
| 358 | # endif |
| 359 | // Load the file. |
| 360 | if (wp->w_buffer->b_ffname != NULL |
| 361 | # ifdef FEAT_QUICKFIX |
| 362 | && !bt_nofilename(wp->w_buffer) |
| 363 | # endif |
| 364 | ) |
| 365 | { |
| 366 | // Editing a file in this buffer: use ":edit file". |
| 367 | // This may have side effects! (e.g., compressed or network file). |
| 368 | // |
| 369 | // Note, if a buffer for that file already exists, use :badd to |
| 370 | // edit that buffer, to not lose folding information (:edit resets |
| 371 | // folds in other buffers) |
| 372 | if (fputs("if bufexists(\"", fd) < 0 |
| 373 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
| 374 | || fputs("\") | buffer ", fd) < 0 |
| 375 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
| 376 | || fputs(" | else | edit ", fd) < 0 |
| 377 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
| 378 | || fputs(" | endif", fd) < 0 |
| 379 | || put_eol(fd) == FAIL) |
| 380 | return FAIL; |
| 381 | } |
| 382 | else |
| 383 | { |
| 384 | // No file in this buffer, just make it empty. |
| 385 | if (put_line(fd, "enew") == FAIL) |
| 386 | return FAIL; |
| 387 | #ifdef FEAT_QUICKFIX |
| 388 | if (wp->w_buffer->b_ffname != NULL) |
| 389 | { |
| 390 | // The buffer does have a name, but it's not a file name. |
| 391 | if (fputs("file ", fd) < 0 |
| 392 | || ses_fname(fd, wp->w_buffer, flagp, TRUE) == FAIL) |
| 393 | return FAIL; |
| 394 | } |
| 395 | #endif |
| 396 | do_cursor = FALSE; |
| 397 | } |
| 398 | } |
| 399 | |
Bram Moolenaar | 59d8e56 | 2020-11-07 18:41:10 +0100 | [diff] [blame] | 400 | if (wp->w_alt_fnum) |
| 401 | { |
| 402 | buf_T *alt = buflist_findnr(wp->w_alt_fnum); |
| 403 | |
| 404 | // Set the alternate file. |
| 405 | if (alt != NULL |
| 406 | && alt->b_fname != NULL |
| 407 | && *alt->b_fname != NUL |
| 408 | && (fputs("balt ", fd) < 0 |
| 409 | || ses_fname(fd, alt, flagp, TRUE) == FAIL)) |
| 410 | return FAIL; |
| 411 | } |
| 412 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 413 | // Local mappings and abbreviations. |
| 414 | if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) |
| 415 | && makemap(fd, wp->w_buffer) == FAIL) |
| 416 | return FAIL; |
| 417 | |
| 418 | // Local options. Need to go to the window temporarily. |
| 419 | // Store only local values when using ":mkview" and when ":mksession" is |
| 420 | // used and 'sessionoptions' doesn't include "options". |
| 421 | // Some folding options are always stored when "folds" is included, |
| 422 | // otherwise the folds would not be restored correctly. |
| 423 | save_curwin = curwin; |
| 424 | curwin = wp; |
| 425 | curbuf = curwin->w_buffer; |
| 426 | if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) |
| 427 | f = makeset(fd, OPT_LOCAL, |
| 428 | flagp == &vop_flags || !(*flagp & SSOP_OPTIONS)); |
| 429 | #ifdef FEAT_FOLDING |
| 430 | else if (*flagp & SSOP_FOLDS) |
| 431 | f = makefoldset(fd); |
| 432 | #endif |
| 433 | else |
| 434 | f = OK; |
| 435 | curwin = save_curwin; |
| 436 | curbuf = curwin->w_buffer; |
| 437 | if (f == FAIL) |
| 438 | return FAIL; |
| 439 | |
| 440 | #ifdef FEAT_FOLDING |
| 441 | // Save Folds when 'buftype' is empty and for help files. |
| 442 | if ((*flagp & SSOP_FOLDS) |
| 443 | && wp->w_buffer->b_ffname != NULL |
| 444 | && (bt_normal(wp->w_buffer) || bt_help(wp->w_buffer))) |
| 445 | { |
| 446 | if (put_folds(fd, wp) == FAIL) |
| 447 | return FAIL; |
| 448 | } |
| 449 | #endif |
| 450 | |
| 451 | // Set the cursor after creating folds, since that moves the cursor. |
| 452 | if (do_cursor) |
| 453 | { |
| 454 | |
| 455 | // Restore the cursor line in the file and relatively in the |
| 456 | // window. Don't use "G", it changes the jumplist. |
| 457 | if (fprintf(fd, "let s:l = %ld - ((%ld * winheight(0) + %ld) / %ld)", |
| 458 | (long)wp->w_cursor.lnum, |
| 459 | (long)(wp->w_cursor.lnum - wp->w_topline), |
| 460 | (long)wp->w_height / 2, (long)wp->w_height) < 0 |
| 461 | || put_eol(fd) == FAIL |
| 462 | || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL |
| 463 | || put_line(fd, "exe s:l") == FAIL |
| 464 | || put_line(fd, "normal! zt") == FAIL |
| 465 | || fprintf(fd, "%ld", (long)wp->w_cursor.lnum) < 0 |
| 466 | || put_eol(fd) == FAIL) |
| 467 | return FAIL; |
| 468 | // Restore the cursor column and left offset when not wrapping. |
| 469 | if (wp->w_cursor.col == 0) |
| 470 | { |
| 471 | if (put_line(fd, "normal! 0") == FAIL) |
| 472 | return FAIL; |
| 473 | } |
| 474 | else |
| 475 | { |
| 476 | if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) |
| 477 | { |
| 478 | if (fprintf(fd, |
| 479 | "let s:c = %ld - ((%ld * winwidth(0) + %ld) / %ld)", |
| 480 | (long)wp->w_virtcol + 1, |
| 481 | (long)(wp->w_virtcol - wp->w_leftcol), |
| 482 | (long)wp->w_width / 2, (long)wp->w_width) < 0 |
| 483 | || put_eol(fd) == FAIL |
| 484 | || put_line(fd, "if s:c > 0") == FAIL |
| 485 | || fprintf(fd, |
| 486 | " exe 'normal! ' . s:c . '|zs' . %ld . '|'", |
| 487 | (long)wp->w_virtcol + 1) < 0 |
| 488 | || put_eol(fd) == FAIL |
| 489 | || put_line(fd, "else") == FAIL |
| 490 | || put_view_curpos(fd, wp, " ") == FAIL |
| 491 | || put_line(fd, "endif") == FAIL) |
| 492 | return FAIL; |
| 493 | } |
| 494 | else if (put_view_curpos(fd, wp, "") == FAIL) |
| 495 | return FAIL; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | // Local directory, if the current flag is not view options or the "curdir" |
| 500 | // option is included. |
| 501 | if (wp->w_localdir != NULL |
| 502 | && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) |
| 503 | { |
| 504 | if (fputs("lcd ", fd) < 0 |
| 505 | || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL |
| 506 | || put_eol(fd) == FAIL) |
| 507 | return FAIL; |
| 508 | did_lcd = TRUE; |
| 509 | } |
| 510 | |
| 511 | return OK; |
| 512 | } |
| 513 | |
| 514 | #ifdef FEAT_EVAL |
| 515 | static int |
| 516 | store_session_globals(FILE *fd) |
| 517 | { |
Bram Moolenaar | da6c033 | 2019-09-01 16:01:30 +0200 | [diff] [blame] | 518 | hashtab_T *gvht = get_globvar_ht(); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 519 | hashitem_T *hi; |
| 520 | dictitem_T *this_var; |
| 521 | int todo; |
| 522 | char_u *p, *t; |
| 523 | |
Bram Moolenaar | da6c033 | 2019-09-01 16:01:30 +0200 | [diff] [blame] | 524 | todo = (int)gvht->ht_used; |
| 525 | for (hi = gvht->ht_array; todo > 0; ++hi) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 526 | { |
| 527 | if (!HASHITEM_EMPTY(hi)) |
| 528 | { |
| 529 | --todo; |
| 530 | this_var = HI2DI(hi); |
| 531 | if ((this_var->di_tv.v_type == VAR_NUMBER |
| 532 | || this_var->di_tv.v_type == VAR_STRING) |
| 533 | && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) |
| 534 | { |
| 535 | // Escape special characters with a backslash. Turn a LF and |
| 536 | // CR into \n and \r. |
| 537 | p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), |
| 538 | (char_u *)"\\\"\n\r"); |
| 539 | if (p == NULL) // out of memory |
| 540 | break; |
| 541 | for (t = p; *t != NUL; ++t) |
| 542 | if (*t == '\n') |
| 543 | *t = 'n'; |
| 544 | else if (*t == '\r') |
| 545 | *t = 'r'; |
| 546 | if ((fprintf(fd, "let %s = %c%s%c", |
| 547 | this_var->di_key, |
| 548 | (this_var->di_tv.v_type == VAR_STRING) ? '"' |
| 549 | : ' ', |
| 550 | p, |
| 551 | (this_var->di_tv.v_type == VAR_STRING) ? '"' |
| 552 | : ' ') < 0) |
| 553 | || put_eol(fd) == FAIL) |
| 554 | { |
| 555 | vim_free(p); |
| 556 | return FAIL; |
| 557 | } |
| 558 | vim_free(p); |
| 559 | } |
| 560 | #ifdef FEAT_FLOAT |
| 561 | else if (this_var->di_tv.v_type == VAR_FLOAT |
| 562 | && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) |
| 563 | { |
| 564 | float_T f = this_var->di_tv.vval.v_float; |
| 565 | int sign = ' '; |
| 566 | |
| 567 | if (f < 0) |
| 568 | { |
| 569 | f = -f; |
| 570 | sign = '-'; |
| 571 | } |
| 572 | if ((fprintf(fd, "let %s = %c%f", |
| 573 | this_var->di_key, sign, f) < 0) |
| 574 | || put_eol(fd) == FAIL) |
| 575 | return FAIL; |
| 576 | } |
| 577 | #endif |
| 578 | } |
| 579 | } |
| 580 | return OK; |
| 581 | } |
| 582 | #endif |
| 583 | |
| 584 | /* |
| 585 | * Write openfile commands for the current buffers to an .exrc file. |
| 586 | * Return FAIL on error, OK otherwise. |
| 587 | */ |
| 588 | static int |
| 589 | makeopens( |
| 590 | FILE *fd, |
| 591 | char_u *dirnow) // Current directory name |
| 592 | { |
| 593 | buf_T *buf; |
| 594 | int only_save_windows = TRUE; |
| 595 | int nr; |
| 596 | int restore_size = TRUE; |
| 597 | win_T *wp; |
| 598 | char_u *sname; |
| 599 | win_T *edited_win = NULL; |
| 600 | int tabnr; |
| 601 | int restore_stal = FALSE; |
| 602 | win_T *tab_firstwin; |
| 603 | frame_T *tab_topframe; |
| 604 | int cur_arg_idx = 0; |
| 605 | int next_arg_idx = 0; |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 606 | int ret = FAIL; |
| 607 | #ifdef FEAT_TERMINAL |
| 608 | hashtab_T terminal_bufs; |
| 609 | |
| 610 | hash_init(&terminal_bufs); |
| 611 | #endif |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 612 | |
| 613 | if (ssop_flags & SSOP_BUFFERS) |
| 614 | only_save_windows = FALSE; // Save ALL buffers |
| 615 | |
| 616 | // Begin by setting the this_session variable, and then other |
| 617 | // sessionable variables. |
| 618 | #ifdef FEAT_EVAL |
| 619 | if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 620 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 621 | if (ssop_flags & SSOP_GLOBALS) |
| 622 | if (store_session_globals(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 623 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 624 | #endif |
| 625 | |
| 626 | // Close all windows and tabs but one. |
| 627 | if (put_line(fd, "silent only") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 628 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 629 | if ((ssop_flags & SSOP_TABPAGES) |
| 630 | && put_line(fd, "silent tabonly") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 631 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 632 | |
| 633 | // Now a :cd command to the session directory or the current directory |
| 634 | if (ssop_flags & SSOP_SESDIR) |
| 635 | { |
| 636 | if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')") |
| 637 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 638 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 639 | } |
| 640 | else if (ssop_flags & SSOP_CURDIR) |
| 641 | { |
| 642 | sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow); |
| 643 | if (sname == NULL |
| 644 | || fputs("cd ", fd) < 0 |
| 645 | || ses_put_fname(fd, sname, &ssop_flags) == FAIL |
| 646 | || put_eol(fd) == FAIL) |
| 647 | { |
| 648 | vim_free(sname); |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 649 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 650 | } |
| 651 | vim_free(sname); |
| 652 | } |
| 653 | |
| 654 | // If there is an empty, unnamed buffer we will wipe it out later. |
| 655 | // Remember the buffer number. |
| 656 | if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 657 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 658 | if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 659 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 660 | if (put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 661 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 662 | |
| 663 | // Now save the current files, current buffer first. |
| 664 | if (put_line(fd, "set shortmess=aoO") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 665 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 666 | |
| 667 | // the global argument list |
| 668 | if (ses_arglist(fd, "argglobal", &global_alist.al_ga, |
| 669 | !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 670 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 671 | |
| 672 | if (ssop_flags & SSOP_RESIZE) |
| 673 | { |
| 674 | // Note: after the restore we still check it worked! |
| 675 | if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0 |
| 676 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 677 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 678 | } |
| 679 | |
| 680 | #ifdef FEAT_GUI |
| 681 | if (gui.in_use && (ssop_flags & SSOP_WINPOS)) |
| 682 | { |
| 683 | int x, y; |
| 684 | |
| 685 | if (gui_mch_get_winpos(&x, &y) == OK) |
| 686 | { |
| 687 | // Note: after the restore we still check it worked! |
| 688 | if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 689 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 690 | } |
| 691 | } |
| 692 | #endif |
| 693 | |
| 694 | // When there are two or more tabpages and 'showtabline' is 1 the tabline |
| 695 | // will be displayed when creating the next tab. That resizes the windows |
| 696 | // in the first tab, which may cause problems. Set 'showtabline' to 2 |
| 697 | // temporarily to avoid that. |
| 698 | if (p_stal == 1 && first_tabpage->tp_next != NULL) |
| 699 | { |
| 700 | if (put_line(fd, "set stal=2") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 701 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 702 | restore_stal = TRUE; |
| 703 | } |
| 704 | |
| 705 | // May repeat putting Windows for each tab, when "tabpages" is in |
| 706 | // 'sessionoptions'. |
| 707 | // Don't use goto_tabpage(), it may change directory and trigger |
| 708 | // autocommands. |
| 709 | tab_firstwin = firstwin; // first window in tab page "tabnr" |
| 710 | tab_topframe = topframe; |
| 711 | if ((ssop_flags & SSOP_TABPAGES)) |
| 712 | { |
| 713 | tabpage_T *tp; |
| 714 | |
| 715 | // Similar to ses_win_rec() below, populate the tab pages first so |
| 716 | // later local options won't be copied to the new tabs. |
| 717 | FOR_ALL_TABPAGES(tp) |
| 718 | if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 719 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 720 | if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 721 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 722 | } |
| 723 | for (tabnr = 1; ; ++tabnr) |
| 724 | { |
| 725 | tabpage_T *tp = NULL; |
| 726 | int need_tabnext = FALSE; |
| 727 | int cnr = 1; |
| 728 | |
| 729 | if ((ssop_flags & SSOP_TABPAGES)) |
| 730 | { |
| 731 | tp = find_tabpage(tabnr); |
| 732 | |
| 733 | if (tp == NULL) |
| 734 | break; // done all tab pages |
| 735 | if (tp == curtab) |
| 736 | { |
| 737 | tab_firstwin = firstwin; |
| 738 | tab_topframe = topframe; |
| 739 | } |
| 740 | else |
| 741 | { |
| 742 | tab_firstwin = tp->tp_firstwin; |
| 743 | tab_topframe = tp->tp_topframe; |
| 744 | } |
| 745 | if (tabnr > 1) |
| 746 | need_tabnext = TRUE; |
| 747 | } |
| 748 | |
| 749 | // Before creating the window layout, try loading one file. If this |
| 750 | // is aborted we don't end up with a number of useless windows. |
| 751 | // This may have side effects! (e.g., compressed or network file). |
| 752 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 753 | { |
| 754 | if (ses_do_win(wp) |
| 755 | && wp->w_buffer->b_ffname != NULL |
| 756 | && !bt_help(wp->w_buffer) |
| 757 | #ifdef FEAT_QUICKFIX |
| 758 | && !bt_nofilename(wp->w_buffer) |
| 759 | #endif |
| 760 | ) |
| 761 | { |
| 762 | if (need_tabnext && put_line(fd, "tabnext") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 763 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 764 | need_tabnext = FALSE; |
| 765 | |
| 766 | if (fputs("edit ", fd) < 0 |
| 767 | || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE) |
| 768 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 769 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 770 | if (!wp->w_arg_idx_invalid) |
| 771 | edited_win = wp; |
| 772 | break; |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | // If no file got edited create an empty tab page. |
| 777 | if (need_tabnext && put_line(fd, "tabnext") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 778 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 779 | |
| 780 | // Save current window layout. |
| 781 | if (put_line(fd, "set splitbelow splitright") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 782 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 783 | if (ses_win_rec(fd, tab_topframe) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 784 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 785 | if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 786 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 787 | if (!p_spr && put_line(fd, "set nosplitright") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 788 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 789 | |
| 790 | // Check if window sizes can be restored (no windows omitted). |
| 791 | // Remember the window number of the current window after restoring. |
| 792 | nr = 0; |
| 793 | for (wp = tab_firstwin; wp != NULL; wp = W_NEXT(wp)) |
| 794 | { |
| 795 | if (ses_do_win(wp)) |
| 796 | ++nr; |
| 797 | else |
| 798 | restore_size = FALSE; |
| 799 | if (curwin == wp) |
| 800 | cnr = nr; |
| 801 | } |
| 802 | |
| 803 | // Go to the first window. |
| 804 | if (put_line(fd, "wincmd t") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 805 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 806 | |
| 807 | // If more than one window, see if sizes can be restored. |
| 808 | // First set 'winheight' and 'winwidth' to 1 to avoid the windows being |
| 809 | // resized when moving between windows. |
| 810 | // Do this before restoring the view, so that the topline and the |
| 811 | // cursor can be set. This is done again below. |
| 812 | // winminheight and winminwidth need to be set to avoid an error if the |
| 813 | // user has set winheight or winwidth. |
| 814 | if (put_line(fd, "set winminheight=0") == FAIL |
| 815 | || put_line(fd, "set winheight=1") == FAIL |
| 816 | || put_line(fd, "set winminwidth=0") == FAIL |
| 817 | || put_line(fd, "set winwidth=1") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 818 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 819 | if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 820 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 821 | |
| 822 | // Restore the tab-local working directory if specified |
| 823 | // Do this before the windows, so that the window-local directory can |
| 824 | // override the tab-local directory. |
| 825 | if (tp != NULL && tp->tp_localdir != NULL && ssop_flags & SSOP_CURDIR) |
| 826 | { |
| 827 | if (fputs("tcd ", fd) < 0 |
| 828 | || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL |
| 829 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 830 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 831 | did_lcd = TRUE; |
| 832 | } |
| 833 | |
| 834 | // Restore the view of the window (options, file, cursor, etc.). |
| 835 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 836 | { |
| 837 | if (!ses_do_win(wp)) |
| 838 | continue; |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 839 | if (put_view(fd, wp, wp != edited_win, &ssop_flags, cur_arg_idx, |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 840 | #ifdef FEAT_TERMINAL |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 841 | &terminal_bufs |
| 842 | #else |
| 843 | NULL |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 844 | #endif |
| 845 | ) == FAIL) |
| 846 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 847 | if (nr > 1 && put_line(fd, "wincmd w") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 848 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 849 | next_arg_idx = wp->w_arg_idx; |
| 850 | } |
| 851 | |
| 852 | // The argument index in the first tab page is zero, need to set it in |
| 853 | // each window. For further tab pages it's the window where we do |
| 854 | // "tabedit". |
| 855 | cur_arg_idx = next_arg_idx; |
| 856 | |
| 857 | // Restore cursor to the current window if it's not the first one. |
| 858 | if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0 |
| 859 | || put_eol(fd) == FAIL)) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 860 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 861 | |
| 862 | // Restore window sizes again after jumping around in windows, because |
| 863 | // the current window has a minimum size while others may not. |
| 864 | if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 865 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 866 | |
| 867 | // Don't continue in another tab page when doing only the current one |
| 868 | // or when at the last tab page. |
| 869 | if (!(ssop_flags & SSOP_TABPAGES)) |
| 870 | break; |
| 871 | } |
| 872 | |
| 873 | if (ssop_flags & SSOP_TABPAGES) |
| 874 | { |
| 875 | if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0 |
| 876 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 877 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 878 | } |
| 879 | if (restore_stal && put_line(fd, "set stal=1") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 880 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 881 | |
| 882 | // Now put the remaining buffers into the buffer list. |
| 883 | // This is near the end, so that when 'hidden' is set we don't create extra |
| 884 | // buffers. If the buffer was already created with another command the |
| 885 | // ":badd" will have no effect. |
| 886 | FOR_ALL_BUFFERS(buf) |
| 887 | { |
| 888 | if (!(only_save_windows && buf->b_nwindows == 0) |
| 889 | && !(buf->b_help && !(ssop_flags & SSOP_HELP)) |
| 890 | #ifdef FEAT_TERMINAL |
| 891 | // Skip terminal buffers: finished ones are not useful, others |
| 892 | // will be resurrected and result in a new buffer. |
| 893 | && !bt_terminal(buf) |
| 894 | #endif |
| 895 | && buf->b_fname != NULL |
| 896 | && buf->b_p_bl) |
| 897 | { |
| 898 | if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L |
| 899 | : buf->b_wininfo->wi_fpos.lnum) < 0 |
| 900 | || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 901 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 902 | } |
| 903 | } |
| 904 | |
| 905 | // Wipe out an empty unnamed buffer we started in. |
| 906 | if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0") |
| 907 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 908 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 909 | if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 910 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 911 | if (put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 912 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 913 | if (put_line(fd, "unlet! s:wipebuf") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 914 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 915 | |
| 916 | // Re-apply 'winheight', 'winwidth' and 'shortmess'. |
| 917 | if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s", |
| 918 | p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 919 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 920 | // Re-apply 'winminheight' and 'winminwidth'. |
| 921 | if (fprintf(fd, "set winminheight=%ld winminwidth=%ld", |
| 922 | p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 923 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 924 | |
| 925 | // Lastly, execute the x.vim file if it exists. |
| 926 | if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL |
Bram Moolenaar | c5f33db | 2020-04-16 21:04:41 +0200 | [diff] [blame] | 927 | || put_line(fd, "if filereadable(s:sx)") == FAIL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 928 | || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL |
| 929 | || put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 930 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 931 | |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 932 | ret = OK; |
| 933 | fail: |
| 934 | #ifdef FEAT_TERMINAL |
| 935 | hash_clear_all(&terminal_bufs, 0); |
| 936 | #endif |
| 937 | return ret; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 938 | } |
| 939 | |
| 940 | /* |
| 941 | * Get the name of the view file for the current buffer. |
| 942 | */ |
| 943 | static char_u * |
| 944 | get_view_file(int c) |
| 945 | { |
| 946 | int len = 0; |
| 947 | char_u *p, *s; |
| 948 | char_u *retval; |
| 949 | char_u *sname; |
| 950 | |
| 951 | if (curbuf->b_ffname == NULL) |
| 952 | { |
| 953 | emsg(_(e_noname)); |
| 954 | return NULL; |
| 955 | } |
| 956 | sname = home_replace_save(NULL, curbuf->b_ffname); |
| 957 | if (sname == NULL) |
| 958 | return NULL; |
| 959 | |
| 960 | // We want a file name without separators, because we're not going to make |
| 961 | // a directory. |
| 962 | // "normal" path separator -> "=+" |
| 963 | // "=" -> "==" |
| 964 | // ":" path separator -> "=-" |
| 965 | for (p = sname; *p; ++p) |
| 966 | if (*p == '=' || vim_ispathsep(*p)) |
| 967 | ++len; |
| 968 | retval = alloc(STRLEN(sname) + len + STRLEN(p_vdir) + 9); |
| 969 | if (retval != NULL) |
| 970 | { |
| 971 | STRCPY(retval, p_vdir); |
| 972 | add_pathsep(retval); |
| 973 | s = retval + STRLEN(retval); |
| 974 | for (p = sname; *p; ++p) |
| 975 | { |
| 976 | if (*p == '=') |
| 977 | { |
| 978 | *s++ = '='; |
| 979 | *s++ = '='; |
| 980 | } |
| 981 | else if (vim_ispathsep(*p)) |
| 982 | { |
| 983 | *s++ = '='; |
| 984 | #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA) || defined(VMS) |
| 985 | if (*p == ':') |
| 986 | *s++ = '-'; |
| 987 | else |
| 988 | #endif |
| 989 | *s++ = '+'; |
| 990 | } |
| 991 | else |
| 992 | *s++ = *p; |
| 993 | } |
| 994 | *s++ = '='; |
| 995 | *s++ = c; |
| 996 | STRCPY(s, ".vim"); |
| 997 | } |
| 998 | |
| 999 | vim_free(sname); |
| 1000 | return retval; |
| 1001 | } |
| 1002 | |
| 1003 | /* |
| 1004 | * ":loadview [nr]" |
| 1005 | */ |
| 1006 | void |
| 1007 | ex_loadview(exarg_T *eap) |
| 1008 | { |
| 1009 | char_u *fname; |
| 1010 | |
| 1011 | fname = get_view_file(*eap->arg); |
| 1012 | if (fname != NULL) |
| 1013 | { |
Bram Moolenaar | 8a7d654 | 2020-01-26 15:56:19 +0100 | [diff] [blame] | 1014 | do_source(fname, FALSE, DOSO_NONE, NULL); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1015 | vim_free(fname); |
| 1016 | } |
| 1017 | } |
| 1018 | |
Bram Moolenaar | d17a57a | 2019-09-29 20:53:55 +0200 | [diff] [blame] | 1019 | # if defined(FEAT_GUI_GNOME) \ |
Bram Moolenaar | ac02a63 | 2019-09-29 19:02:46 +0200 | [diff] [blame] | 1020 | || (defined(GUI_MAY_SPAWN) && defined(EXPERIMENTAL_GUI_CMD)) \ |
| 1021 | || defined(PROTO) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1022 | /* |
| 1023 | * Generate a script that can be used to restore the current editing session. |
| 1024 | * Save the value of v:this_session before running :mksession in order to make |
| 1025 | * automagic session save fully transparent. Return TRUE on success. |
| 1026 | */ |
| 1027 | int |
| 1028 | write_session_file(char_u *filename) |
| 1029 | { |
| 1030 | char_u *escaped_filename; |
| 1031 | char *mksession_cmdline; |
| 1032 | unsigned int save_ssop_flags; |
| 1033 | int failed; |
| 1034 | |
| 1035 | // Build an ex command line to create a script that restores the current |
| 1036 | // session if executed. Escape the filename to avoid nasty surprises. |
| 1037 | escaped_filename = vim_strsave_escaped(filename, escape_chars); |
| 1038 | if (escaped_filename == NULL) |
| 1039 | return FALSE; |
| 1040 | mksession_cmdline = alloc(10 + (int)STRLEN(escaped_filename) + 1); |
| 1041 | if (mksession_cmdline == NULL) |
| 1042 | { |
| 1043 | vim_free(escaped_filename); |
| 1044 | return FALSE; |
| 1045 | } |
| 1046 | strcpy(mksession_cmdline, "mksession "); |
| 1047 | STRCAT(mksession_cmdline, escaped_filename); |
| 1048 | vim_free(escaped_filename); |
| 1049 | |
| 1050 | // Use a reasonable hardcoded set of 'sessionoptions' flags to avoid |
| 1051 | // unpredictable effects when the session is saved automatically. Also, |
| 1052 | // we definitely need SSOP_GLOBALS to be able to restore v:this_session. |
| 1053 | // Don't use SSOP_BUFFERS to prevent the buffer list from becoming |
| 1054 | // enormously large if the GNOME session feature is used regularly. |
| 1055 | save_ssop_flags = ssop_flags; |
| 1056 | ssop_flags = (SSOP_BLANK|SSOP_CURDIR|SSOP_FOLDS|SSOP_GLOBALS |
| 1057 | |SSOP_HELP|SSOP_OPTIONS|SSOP_WINSIZE|SSOP_TABPAGES); |
| 1058 | |
| 1059 | do_cmdline_cmd((char_u *)"let Save_VV_this_session = v:this_session"); |
| 1060 | failed = (do_cmdline_cmd((char_u *)mksession_cmdline) == FAIL); |
| 1061 | do_cmdline_cmd((char_u *)"let v:this_session = Save_VV_this_session"); |
| 1062 | do_unlet((char_u *)"Save_VV_this_session", TRUE); |
| 1063 | |
| 1064 | ssop_flags = save_ssop_flags; |
| 1065 | vim_free(mksession_cmdline); |
| 1066 | |
| 1067 | // Reopen the file and append a command to restore v:this_session, |
| 1068 | // as if this save never happened. This is to avoid conflicts with |
| 1069 | // the user's own sessions. FIXME: It's probably less hackish to add |
| 1070 | // a "stealth" flag to 'sessionoptions' -- gotta ask Bram. |
| 1071 | if (!failed) |
| 1072 | { |
| 1073 | FILE *fd; |
| 1074 | |
| 1075 | fd = open_exfile(filename, TRUE, APPENDBIN); |
| 1076 | |
| 1077 | failed = (fd == NULL |
| 1078 | || put_line(fd, "let v:this_session = Save_VV_this_session") |
| 1079 | == FAIL |
| 1080 | || put_line(fd, "unlet Save_VV_this_session") == FAIL); |
| 1081 | |
| 1082 | if (fd != NULL && fclose(fd) != 0) |
| 1083 | failed = TRUE; |
| 1084 | |
| 1085 | if (failed) |
| 1086 | mch_remove(filename); |
| 1087 | } |
| 1088 | |
| 1089 | return !failed; |
| 1090 | } |
| 1091 | # endif |
| 1092 | |
| 1093 | #endif // FEAT_SESSION |
| 1094 | |
| 1095 | #if defined(FEAT_SESSION) && defined(USE_CRNL) |
| 1096 | # define MKSESSION_NL |
| 1097 | static int mksession_nl = FALSE; // use NL only in put_eol() |
| 1098 | #endif |
| 1099 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1100 | /* |
| 1101 | * ":mkexrc", ":mkvimrc", ":mkview" and ":mksession". |
| 1102 | */ |
| 1103 | void |
| 1104 | ex_mkrc(exarg_T *eap) |
| 1105 | { |
| 1106 | FILE *fd; |
| 1107 | int failed = FALSE; |
| 1108 | char_u *fname; |
| 1109 | #ifdef FEAT_BROWSE |
| 1110 | char_u *browseFile = NULL; |
| 1111 | #endif |
| 1112 | #ifdef FEAT_SESSION |
| 1113 | int view_session = FALSE; |
| 1114 | int using_vdir = FALSE; // using 'viewdir'? |
| 1115 | char_u *viewFile = NULL; |
| 1116 | unsigned *flagp; |
| 1117 | #endif |
| 1118 | |
| 1119 | if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) |
| 1120 | { |
| 1121 | #ifdef FEAT_SESSION |
| 1122 | view_session = TRUE; |
| 1123 | #else |
| 1124 | ex_ni(eap); |
| 1125 | return; |
| 1126 | #endif |
| 1127 | } |
| 1128 | |
| 1129 | #ifdef FEAT_SESSION |
| 1130 | // Use the short file name until ":lcd" is used. We also don't use the |
| 1131 | // short file name when 'acd' is set, that is checked later. |
| 1132 | did_lcd = FALSE; |
| 1133 | |
| 1134 | // ":mkview" or ":mkview 9": generate file name with 'viewdir' |
| 1135 | if (eap->cmdidx == CMD_mkview |
| 1136 | && (*eap->arg == NUL |
| 1137 | || (vim_isdigit(*eap->arg) && eap->arg[1] == NUL))) |
| 1138 | { |
| 1139 | eap->forceit = TRUE; |
| 1140 | fname = get_view_file(*eap->arg); |
| 1141 | if (fname == NULL) |
| 1142 | return; |
| 1143 | viewFile = fname; |
| 1144 | using_vdir = TRUE; |
| 1145 | } |
| 1146 | else |
| 1147 | #endif |
| 1148 | if (*eap->arg != NUL) |
| 1149 | fname = eap->arg; |
| 1150 | else if (eap->cmdidx == CMD_mkvimrc) |
| 1151 | fname = (char_u *)VIMRC_FILE; |
| 1152 | #ifdef FEAT_SESSION |
| 1153 | else if (eap->cmdidx == CMD_mksession) |
| 1154 | fname = (char_u *)SESSION_FILE; |
| 1155 | #endif |
| 1156 | else |
| 1157 | fname = (char_u *)EXRC_FILE; |
| 1158 | |
| 1159 | #ifdef FEAT_BROWSE |
Bram Moolenaar | e100440 | 2020-10-24 20:49:43 +0200 | [diff] [blame] | 1160 | if (cmdmod.cmod_flags & CMOD_BROWSE) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1161 | { |
| 1162 | browseFile = do_browse(BROWSE_SAVE, |
| 1163 | # ifdef FEAT_SESSION |
| 1164 | eap->cmdidx == CMD_mkview ? (char_u *)_("Save View") : |
| 1165 | eap->cmdidx == CMD_mksession ? (char_u *)_("Save Session") : |
| 1166 | # endif |
| 1167 | (char_u *)_("Save Setup"), |
| 1168 | fname, (char_u *)"vim", NULL, |
| 1169 | (char_u *)_(BROWSE_FILTER_MACROS), NULL); |
| 1170 | if (browseFile == NULL) |
| 1171 | goto theend; |
| 1172 | fname = browseFile; |
| 1173 | eap->forceit = TRUE; // since dialog already asked |
| 1174 | } |
| 1175 | #endif |
| 1176 | |
| 1177 | #if defined(FEAT_SESSION) && defined(vim_mkdir) |
| 1178 | // When using 'viewdir' may have to create the directory. |
| 1179 | if (using_vdir && !mch_isdir(p_vdir)) |
| 1180 | vim_mkdir_emsg(p_vdir, 0755); |
| 1181 | #endif |
| 1182 | |
| 1183 | fd = open_exfile(fname, eap->forceit, WRITEBIN); |
| 1184 | if (fd != NULL) |
| 1185 | { |
| 1186 | #ifdef FEAT_SESSION |
| 1187 | if (eap->cmdidx == CMD_mkview) |
| 1188 | flagp = &vop_flags; |
| 1189 | else |
| 1190 | flagp = &ssop_flags; |
| 1191 | #endif |
| 1192 | |
| 1193 | #ifdef MKSESSION_NL |
| 1194 | // "unix" in 'sessionoptions': use NL line separator |
| 1195 | if (view_session && (*flagp & SSOP_UNIX)) |
| 1196 | mksession_nl = TRUE; |
| 1197 | #endif |
| 1198 | |
| 1199 | // Write the version command for :mkvimrc |
| 1200 | if (eap->cmdidx == CMD_mkvimrc) |
| 1201 | (void)put_line(fd, "version 6.0"); |
| 1202 | |
| 1203 | #ifdef FEAT_SESSION |
| 1204 | if (eap->cmdidx == CMD_mksession) |
| 1205 | { |
| 1206 | if (put_line(fd, "let SessionLoad = 1") == FAIL) |
| 1207 | failed = TRUE; |
| 1208 | } |
| 1209 | |
| 1210 | if (eap->cmdidx != CMD_mkview) |
| 1211 | #endif |
| 1212 | { |
| 1213 | // Write setting 'compatible' first, because it has side effects. |
| 1214 | // For that same reason only do it when needed. |
| 1215 | if (p_cp) |
| 1216 | (void)put_line(fd, "if !&cp | set cp | endif"); |
| 1217 | else |
| 1218 | (void)put_line(fd, "if &cp | set nocp | endif"); |
| 1219 | } |
| 1220 | |
| 1221 | #ifdef FEAT_SESSION |
| 1222 | if (!view_session |
| 1223 | || (eap->cmdidx == CMD_mksession |
| 1224 | && (*flagp & SSOP_OPTIONS))) |
| 1225 | #endif |
| 1226 | failed |= (makemap(fd, NULL) == FAIL |
| 1227 | || makeset(fd, OPT_GLOBAL, FALSE) == FAIL); |
| 1228 | |
| 1229 | #ifdef FEAT_SESSION |
| 1230 | if (!failed && view_session) |
| 1231 | { |
Bram Moolenaar | 3889083 | 2020-11-01 17:40:54 +0100 | [diff] [blame] | 1232 | if (put_line(fd, "let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1233 | failed = TRUE; |
| 1234 | if (eap->cmdidx == CMD_mksession) |
| 1235 | { |
| 1236 | char_u *dirnow; // current directory |
| 1237 | |
| 1238 | dirnow = alloc(MAXPATHL); |
| 1239 | if (dirnow == NULL) |
| 1240 | failed = TRUE; |
| 1241 | else |
| 1242 | { |
| 1243 | // Change to session file's dir. |
| 1244 | if (mch_dirname(dirnow, MAXPATHL) == FAIL |
| 1245 | || mch_chdir((char *)dirnow) != 0) |
| 1246 | *dirnow = NUL; |
| 1247 | if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) |
| 1248 | { |
| 1249 | if (vim_chdirfile(fname, NULL) == OK) |
| 1250 | shorten_fnames(TRUE); |
| 1251 | } |
| 1252 | else if (*dirnow != NUL |
| 1253 | && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) |
| 1254 | { |
| 1255 | if (mch_chdir((char *)globaldir) == 0) |
| 1256 | shorten_fnames(TRUE); |
| 1257 | } |
| 1258 | |
| 1259 | failed |= (makeopens(fd, dirnow) == FAIL); |
| 1260 | |
| 1261 | // restore original dir |
| 1262 | if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR) |
| 1263 | || ((ssop_flags & SSOP_CURDIR) && globaldir != NULL))) |
| 1264 | { |
| 1265 | if (mch_chdir((char *)dirnow) != 0) |
| 1266 | emsg(_(e_prev_dir)); |
| 1267 | shorten_fnames(TRUE); |
| 1268 | } |
| 1269 | vim_free(dirnow); |
| 1270 | } |
| 1271 | } |
| 1272 | else |
| 1273 | { |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 1274 | failed |= (put_view(fd, curwin, !using_vdir, flagp, -1, NULL) |
| 1275 | == FAIL); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1276 | } |
Bram Moolenaar | 3889083 | 2020-11-01 17:40:54 +0100 | [diff] [blame] | 1277 | if (put_line(fd, "let &g:so = s:so_save | let &g:siso = s:siso_save") |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1278 | == FAIL) |
| 1279 | failed = TRUE; |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1280 | #ifdef FEAT_SEARCH_EXTRA |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1281 | if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL) |
| 1282 | failed = TRUE; |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1283 | #endif |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1284 | if (put_line(fd, "doautoall SessionLoadPost") == FAIL) |
| 1285 | failed = TRUE; |
| 1286 | if (eap->cmdidx == CMD_mksession) |
| 1287 | { |
| 1288 | if (put_line(fd, "unlet SessionLoad") == FAIL) |
| 1289 | failed = TRUE; |
| 1290 | } |
| 1291 | } |
| 1292 | #endif |
| 1293 | if (put_line(fd, "\" vim: set ft=vim :") == FAIL) |
| 1294 | failed = TRUE; |
| 1295 | |
| 1296 | failed |= fclose(fd); |
| 1297 | |
| 1298 | if (failed) |
| 1299 | emsg(_(e_write)); |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1300 | #if defined(FEAT_SESSION) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1301 | else if (eap->cmdidx == CMD_mksession) |
| 1302 | { |
| 1303 | // successful session write - set this_session var |
| 1304 | char_u *tbuf; |
| 1305 | |
| 1306 | tbuf = alloc(MAXPATHL); |
| 1307 | if (tbuf != NULL) |
| 1308 | { |
| 1309 | if (vim_FullName(fname, tbuf, MAXPATHL, FALSE) == OK) |
| 1310 | set_vim_var_string(VV_THIS_SESSION, tbuf, -1); |
| 1311 | vim_free(tbuf); |
| 1312 | } |
| 1313 | } |
| 1314 | #endif |
| 1315 | #ifdef MKSESSION_NL |
| 1316 | mksession_nl = FALSE; |
| 1317 | #endif |
| 1318 | } |
| 1319 | |
| 1320 | #ifdef FEAT_BROWSE |
| 1321 | theend: |
| 1322 | vim_free(browseFile); |
| 1323 | #endif |
| 1324 | #ifdef FEAT_SESSION |
| 1325 | vim_free(viewFile); |
| 1326 | #endif |
| 1327 | } |
| 1328 | |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1329 | #if (defined(FEAT_VIMINFO) || defined(FEAT_SESSION)) || defined(PROTO) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1330 | var_flavour_T |
| 1331 | var_flavour(char_u *varname) |
| 1332 | { |
| 1333 | char_u *p = varname; |
| 1334 | |
| 1335 | if (ASCII_ISUPPER(*p)) |
| 1336 | { |
| 1337 | while (*(++p)) |
| 1338 | if (ASCII_ISLOWER(*p)) |
| 1339 | return VAR_FLAVOUR_SESSION; |
| 1340 | return VAR_FLAVOUR_VIMINFO; |
| 1341 | } |
| 1342 | else |
| 1343 | return VAR_FLAVOUR_DEFAULT; |
| 1344 | } |
| 1345 | #endif |
| 1346 | |
| 1347 | /* |
| 1348 | * Write end-of-line character(s) for ":mkexrc", ":mkvimrc" and ":mksession". |
| 1349 | * Return FAIL for a write error. |
| 1350 | */ |
| 1351 | int |
| 1352 | put_eol(FILE *fd) |
| 1353 | { |
| 1354 | if ( |
| 1355 | #ifdef USE_CRNL |
| 1356 | ( |
| 1357 | # ifdef MKSESSION_NL |
| 1358 | !mksession_nl && |
| 1359 | # endif |
| 1360 | (putc('\r', fd) < 0)) || |
| 1361 | #endif |
| 1362 | (putc('\n', fd) < 0)) |
| 1363 | return FAIL; |
| 1364 | return OK; |
| 1365 | } |
| 1366 | |
| 1367 | /* |
| 1368 | * Write a line to "fd". |
| 1369 | * Return FAIL for a write error. |
| 1370 | */ |
| 1371 | int |
| 1372 | put_line(FILE *fd, char *s) |
| 1373 | { |
| 1374 | if (fputs(s, fd) < 0 || put_eol(fd) == FAIL) |
| 1375 | return FAIL; |
| 1376 | return OK; |
| 1377 | } |