Merge branch 'for-steve' into njsf-cov
[sxemacs] / src / editfns.c
1 /* Lisp functions pertaining to editing.
2    Copyright (C) 1985-1987, 1989, 1992-1995 Free Software Foundation, Inc.
3    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
4    Copyright (C) 1996 Ben Wing.
5
6 This file is part of SXEmacs
7
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
20
21
22 /* Synched up with: Mule 2.0, FSF 19.30. */
23
24 /* This file has been Mule-ized. */
25
26 /* Hacked on for Mule by Ben Wing, December 1994. */
27
28 #include <config.h>
29 #include "lisp.h"
30
31 #include "buffer.h"
32 #include "commands.h"
33 #define INCLUDE_EVENTS_H_PRIVATE_SPHERE
34 #include "events/events.h"              /* for EVENTP */
35 #include "extents.h"
36 #include "ui/frame.h"
37 #include "ui/insdel.h"
38 #include "ui/window.h"
39 #include "casetab.h"
40 #include "chartab.h"
41 #include "line-number.h"
42
43 #include "systime.h"
44 #include "sysdep.h"
45 #include "syspwd.h"
46 #include "sysfile.h"            /* for getcwd */
47
48 /* Some static data, and a function to initialize it for each run */
49
50 Lisp_Object Vsystem_name;       /* #### - I don't see why this should be */
51                                 /* static, either...  --Stig */
52 #if 0                           /* XEmacs - this is now dynamic */
53                                 /* if at some point it's deemed desirable to
54                                    use lisp variables here, then they can be
55                                    initialized to nil and then set to their
56                                    real values upon the first call to the
57                                    functions that generate them. --stig */
58 Lisp_Object Vuser_real_login_name;      /* login name of current user ID */
59 Lisp_Object Vuser_login_name;   /* user name from LOGNAME or USER.  */
60 #endif
61
62 /* It's useful to be able to set this as user customization, so we'll
63    keep it. */
64 Lisp_Object Vuser_full_name;
65 EXFUN(Fuser_full_name, 1);
66
67 Lisp_Object Qformat;
68
69 Lisp_Object Qpoint, Qmark, Qregion_beginning, Qregion_end;
70
71 Lisp_Object Quser_files_and_directories;
72
73 /* This holds the value of `environ' produced by the previous
74    call to Fset_time_zone_rule, or 0 if Fset_time_zone_rule
75    has never been called.  */
76 static char **environbuf;
77
78 void init_editfns(void)
79 {
80 /* Only used in removed code below. */
81         char *p;
82
83         environbuf = 0;
84
85         /* Set up system_name even when dumping.  */
86         init_system_name();
87
88 #ifndef CANNOT_DUMP
89         if (!initialized)
90                 return;
91 #endif
92
93         if ((p = getenv("NAME")))
94                 /* I don't think it's the right thing to do the ampersand
95                    modification on NAME.  Not that it matters anymore...  -hniksic */
96                 Vuser_full_name = build_ext_string(p, Qnative);
97         else
98                 Vuser_full_name = Fuser_full_name(Qnil);
99 }
100 \f
101 DEFUN("char-to-string", Fchar_to_string, 1, 1, 0,       /*
102 Convert CHARACTER to a one-character string containing that character.
103 */
104       (character))
105 {
106         Bytecount len;
107         Bufbyte str[MAX_EMCHAR_LEN];
108
109         if (EVENTP(character)) {
110                 Lisp_Object ch2 =
111                     Fevent_to_character(character, Qt, Qnil, Qnil);
112                 if (NILP(ch2))
113                         return
114                             signal_simple_continuable_error
115                             ("character has no ASCII equivalent:",
116                              Fcopy_event(character, Qnil));
117                 character = ch2;
118         }
119
120         CHECK_CHAR_COERCE_INT(character);
121
122         len = set_charptr_emchar(str, XCHAR(character));
123         return make_string(str, len);
124 }
125
126 DEFUN("string-to-char", Fstring_to_char, 1, 1, 0,       /*
127 Convert arg STRING to a character, the first character of that string.
128 An empty string will return the constant `nil'.
129 */
130       (string))
131 {
132         Lisp_String *p;
133         CHECK_STRING(string);
134
135         p = XSTRING(string);
136         if (string_length(p) != 0)
137                 return make_char(string_char(p, 0));
138         else
139                 /* This used to return Qzero.  That is broken, broken, broken. */
140                 /* It might be kinder to signal an error directly. -slb */
141                 return Qnil;
142 }
143 \f
144 static Lisp_Object buildmark(Bufpos val, Lisp_Object buffer)
145 {
146         Lisp_Object mark = Fmake_marker();
147         Fset_marker(mark, make_int(val), buffer);
148         return mark;
149 }
150
151 DEFUN("point", Fpoint, 0, 1, 0, /*
152 Return value of point, as an integer.
153 Beginning of buffer is position (point-min).
154 If BUFFER is nil, the current buffer is assumed.
155 */
156       (buffer))
157 {
158         struct buffer *b = decode_buffer(buffer, 1);
159         return make_int(BUF_PT(b));
160 }
161
162 DEFUN("point-marker", Fpoint_marker, 0, 2, 0,   /*
163 Return value of point, as a marker object.
164 This marker is a copy; you may modify it with reckless abandon.
165 If optional argument DONT-COPY-P is non-nil, then it returns the real
166 point-marker; modifying the position of this marker will move point.
167 It is illegal to change the buffer of it, or make it point nowhere.
168 If BUFFER is nil, the current buffer is assumed.
169 */
170       (dont_copy_p, buffer))
171 {
172         struct buffer *b = decode_buffer(buffer, 1);
173         if (NILP(dont_copy_p))
174                 return Fcopy_marker(b->point_marker, Qnil);
175         else
176                 return b->point_marker;
177 }
178
179 /* The following two functions end up being identical but it's
180    cleaner to declare them separately. */
181
182 Bufpos bufpos_clip_to_bounds(Bufpos lower, Bufpos num, Bufpos upper)
183 {
184         return (num < lower ? lower : num > upper ? upper : num);
185 }
186
187 Bytind bytind_clip_to_bounds(Bytind lower, Bytind num, Bytind upper)
188 {
189         return (num < lower ? lower : num > upper ? upper : num);
190 }
191
192 /*
193  * Chuck says:
194  * There is no absolute way to determine if goto-char is the function
195  * being run.  this-command doesn't work because it is often eval'd
196  * and this-command ends up set to eval-expression.  So this flag gets
197  * added for now.
198  *
199  * Jamie thinks he's wrong, but we'll leave this in for now.
200  */
201 int atomic_extent_goto_char_p;
202
203 DEFUN("goto-char", Fgoto_char, 1, 2, "NGoto char: ",    /*
204 Set point to POSITION, a number or marker.
205 Beginning of buffer is position (point-min), end is (point-max).
206 If BUFFER is nil, the current buffer is assumed.
207 Return value of POSITION, as an integer.
208 */
209       (position, buffer))
210 {
211         struct buffer *b = decode_buffer(buffer, 1);
212         Bufpos n = get_buffer_pos_char(b, position, GB_COERCE_RANGE);
213         BUF_SET_PT(b, n);
214         atomic_extent_goto_char_p = 1;
215         return make_int(n);
216 }
217
218 static Lisp_Object region_limit(int beginningp, struct buffer *b)
219 {
220         Lisp_Object m;
221
222 #if 0                           /* FSFmacs */
223         if (!NILP(Vtransient_mark_mode) && NILP(Vmark_even_if_inactive)
224             && NILP(b->mark_active))
225                 Fsignal(Qmark_inactive, Qnil);
226 #endif
227         m = Fmarker_position(b->mark);
228         if (NILP(m))
229                 error("There is no region now");
230         if (!!(BUF_PT(b) < XINT(m)) == !!beginningp)
231                 return make_int(BUF_PT(b));
232         else
233                 return m;
234 }
235
236 DEFUN("region-beginning", Fregion_beginning, 0, 1, 0,   /*
237 Return position of beginning of region in BUFFER, as an integer.
238 If BUFFER is nil, the current buffer is assumed.
239 */
240       (buffer))
241 {
242         return region_limit(1, decode_buffer(buffer, 1));
243 }
244
245 DEFUN("region-end", Fregion_end, 0, 1, 0,       /*
246 Return position of end of region in BUFFER, as an integer.
247 If BUFFER is nil, the current buffer is assumed.
248 */
249       (buffer))
250 {
251         return region_limit(0, decode_buffer(buffer, 1));
252 }
253
254 /* Whether to use lispm-style active-regions */
255 int zmacs_regions;
256
257 /* Whether the zmacs region is active.  This is not per-buffer because
258    there can be only one active region at a time.  #### Now that the
259    zmacs region are not directly tied to the X selections this may not
260    necessarily have to be true.  */
261 int zmacs_region_active_p;
262
263 int zmacs_region_stays;
264
265 Lisp_Object Qzmacs_update_region, Qzmacs_deactivate_region;
266 Lisp_Object Qzmacs_region_buffer;
267
268 void zmacs_update_region(void)
269 {
270         /* This function can GC */
271         if (zmacs_region_active_p)
272                 call0(Qzmacs_update_region);
273 }
274
275 void zmacs_deactivate_region(void)
276 {
277         /* This function can GC */
278         if (zmacs_region_active_p)
279                 call0(Qzmacs_deactivate_region);
280 }
281
282 Lisp_Object zmacs_region_buffer(void)
283 {
284         if (zmacs_region_active_p)
285                 return call0(Qzmacs_region_buffer);
286         else
287                 return Qnil;
288 }
289
290 DEFUN("mark-marker", Fmark_marker, 0, 2, 0,     /*
291 Return this buffer's mark, as a marker object.
292 If `zmacs-regions' is true, then this returns nil unless the region is
293 currently in the active (highlighted) state.  If optional argument FORCE
294 is t, this returns the mark (if there is one) regardless of the zmacs-region
295 state.  You should *generally* not use the mark unless the region is active,
296 if the user has expressed a preference for the zmacs-region model.
297 Watch out!  Moving this marker changes the mark position.
298 If you set the marker not to point anywhere, the buffer will have no mark.
299 If BUFFER is nil, the current buffer is assumed.
300 */
301       (force, buffer))
302 {
303         struct buffer *b = decode_buffer(buffer, 1);
304         if (!zmacs_regions || zmacs_region_active_p || !NILP(force))
305                 return b->mark;
306         return Qnil;
307 }
308 \f
309 /* The saved object is a cons:
310
311    (COPY-OF-POINT-MARKER . COPY-OF-MARK)
312
313    We used to have another cons for a VISIBLE-P element, which was t
314    if `(eq (current-buffer) (window-buffer (selected-window)))' but it
315    was unused for a long time, so I removed it.  --hniksic */
316 Lisp_Object save_excursion_save(void)
317 {
318         struct buffer *b;
319
320         /* #### Huh?  --hniksic */
321         /*if (preparing_for_armageddon) return Qnil; */
322
323 #ifdef ERROR_CHECK_BUFPOS
324         assert(XINT(Fpoint(Qnil)) ==
325                XINT(Fmarker_position(Fpoint_marker(Qt, Qnil))));
326 #endif
327
328         b = current_buffer;
329
330         return noseeum_cons(noseeum_copy_marker(b->point_marker, Qnil),
331                             noseeum_copy_marker(b->mark, Qnil));
332 }
333
334 Lisp_Object save_excursion_restore(Lisp_Object info)
335 {
336         Lisp_Object buffer = Fmarker_buffer(XCAR(info));
337
338         /* If buffer being returned to is now deleted, avoid error --
339            otherwise could get error here while unwinding to top level and
340            crash.  In that case, Fmarker_buffer returns nil now.  */
341         if (!NILP(buffer)) {
342                 struct buffer *buf = XBUFFER(buffer);
343                 struct gcpro gcpro1;
344                 GCPRO1(info);
345                 set_buffer_internal(buf);
346                 Fgoto_char(XCAR(info), buffer);
347                 Fset_marker(buf->mark, XCDR(info), buffer);
348
349 #if 0                           /* We used to make the current buffer visible in the selected window
350                                    if that was true previously.  That avoids some anomalies.
351                                    But it creates others, and it wasn't documented, and it is simpler
352                                    and cleaner never to alter the window/buffer connections.  */
353                 /* I'm certain some code somewhere depends on this behavior. --jwz */
354                 /* Even if it did, it certainly doesn't matter anymore, because
355                    this has been the behavior for countless XEmacs releases
356                    now.  --hniksic */
357                 if (visible
358                     && (current_buffer !=
359                         XBUFFER(XWINDOW(selected_window)->buffer)))
360                         switch_to_buffer(Fcurrent_buffer(), Qnil);
361 #endif
362
363                 UNGCPRO;
364         }
365
366         /* Free all the junk we allocated, so that a `save-excursion' comes
367            for free in terms of GC junk. */
368         free_marker(XMARKER(XCAR(info)));
369         free_marker(XMARKER(XCDR(info)));
370         free_cons(XCONS(info));
371         return Qnil;
372 }
373
374 DEFUN("save-excursion", Fsave_excursion, 0, UNEVALLED, 0,       /*
375 Save point, mark, and current buffer; execute BODY; restore those things.
376 Executes BODY just like `progn'.
377 The values of point, mark and the current buffer are restored
378 even in case of abnormal exit (throw or error).
379 */
380       (args))
381 {
382         /* This function can GC */
383         int speccount = specpdl_depth();
384
385         record_unwind_protect(save_excursion_restore, save_excursion_save());
386
387         return unbind_to(speccount, Fprogn(args));
388 }
389
390 Lisp_Object save_current_buffer_restore(Lisp_Object buffer)
391 {
392         struct buffer *buf = XBUFFER(buffer);
393         /* Avoid signaling an error if the buffer is no longer alive.  This
394            is for consistency with save-excursion.  */
395         if (BUFFER_LIVE_P(buf))
396                 set_buffer_internal(buf);
397         return Qnil;
398 }
399
400 DEFUN("save-current-buffer", Fsave_current_buffer, 0, UNEVALLED, 0,     /*
401 Save the current buffer; execute BODY; restore the current buffer.
402 Executes BODY just like `progn'.
403 */
404       (args))
405 {
406         /* This function can GC */
407         int speccount = specpdl_depth();
408
409         record_unwind_protect(save_current_buffer_restore, Fcurrent_buffer());
410
411         return unbind_to(speccount, Fprogn(args));
412 }
413 \f
414 DEFUN("buffer-size", Fbuffer_size, 0, 1, 0,     /*
415 Return the number of characters in BUFFER.
416 If BUFFER is nil, the current buffer is assumed.
417 */
418       (buffer))
419 {
420         struct buffer *b = decode_buffer(buffer, 1);
421         return make_int(BUF_SIZE(b));
422 }
423
424 DEFUN("point-min", Fpoint_min, 0, 1, 0, /*
425 Return the minimum permissible value of point in BUFFER.
426 This is 1, unless narrowing (a buffer restriction)
427 is in effect, in which case it may be greater.
428 If BUFFER is nil, the current buffer is assumed.
429 */
430       (buffer))
431 {
432         struct buffer *b = decode_buffer(buffer, 1);
433         return make_int(BUF_BEGV(b));
434 }
435
436 DEFUN("point-min-marker", Fpoint_min_marker, 0, 1, 0,   /*
437 Return a marker to the minimum permissible value of point in BUFFER.
438 This is the beginning, unless narrowing (a buffer restriction)
439 is in effect, in which case it may be greater.
440 If BUFFER is nil, the current buffer is assumed.
441 */
442       (buffer))
443 {
444         struct buffer *b = decode_buffer(buffer, 1);
445         return buildmark(BUF_BEGV(b), make_buffer(b));
446 }
447
448 DEFUN("point-max", Fpoint_max, 0, 1, 0, /*
449 Return the maximum permissible value of point in BUFFER.
450 This is (1+ (buffer-size)), unless narrowing (a buffer restriction)
451 is in effect, in which case it may be less.
452 If BUFFER is nil, the current buffer is assumed.
453 */
454       (buffer))
455 {
456         struct buffer *b = decode_buffer(buffer, 1);
457         return make_int(BUF_ZV(b));
458 }
459
460 DEFUN("point-max-marker", Fpoint_max_marker, 0, 1, 0,   /*
461 Return a marker to the maximum permissible value of point in BUFFER.
462 This is (1+ (buffer-size)), unless narrowing (a buffer restriction)
463 is in effect, in which case it may be less.
464 If BUFFER is nil, the current buffer is assumed.
465 */
466       (buffer))
467 {
468         struct buffer *b = decode_buffer(buffer, 1);
469         return buildmark(BUF_ZV(b), make_buffer(b));
470 }
471
472 DEFUN("following-char", Ffollowing_char, 0, 1, 0,       /*
473 Return the character following point.
474 At the end of the buffer or accessible region, return 0.
475 If BUFFER is nil, the current buffer is assumed.
476 */
477       (buffer))
478 {
479         struct buffer *b = decode_buffer(buffer, 1);
480         if (BUF_PT(b) >= BUF_ZV(b))
481                 return Qzero;   /* #### Gag me! */
482         else
483                 return make_char(BUF_FETCH_CHAR(b, BUF_PT(b)));
484 }
485
486 DEFUN("preceding-char", Fpreceding_char, 0, 1, 0,       /*
487 Return the character preceding point.
488 At the beginning of the buffer or accessible region, return 0.
489 If BUFFER is nil, the current buffer is assumed.
490 */
491       (buffer))
492 {
493         struct buffer *b = decode_buffer(buffer, 1);
494         if (BUF_PT(b) <= BUF_BEGV(b))
495                 return Qzero;   /* #### Gag me! */
496         else
497                 return make_char(BUF_FETCH_CHAR(b, BUF_PT(b) - 1));
498 }
499
500 DEFUN("bobp", Fbobp, 0, 1, 0,   /*
501 Return t if point is at the beginning of the buffer.
502 If the buffer is narrowed, this means the beginning of the narrowed part.
503 If BUFFER is nil, the current buffer is assumed.
504 */
505       (buffer))
506 {
507         struct buffer *b = decode_buffer(buffer, 1);
508         return BUF_PT(b) == BUF_BEGV(b) ? Qt : Qnil;
509 }
510
511 DEFUN("eobp", Feobp, 0, 1, 0,   /*
512 Return t if point is at the end of the buffer.
513 If the buffer is narrowed, this means the end of the narrowed part.
514 If BUFFER is nil, the current buffer is assumed.
515 */
516       (buffer))
517 {
518         struct buffer *b = decode_buffer(buffer, 1);
519         return BUF_PT(b) == BUF_ZV(b) ? Qt : Qnil;
520 }
521
522 int beginning_of_line_p(struct buffer *b, Bufpos pt)
523 {
524         return pt <= BUF_BEGV(b) || BUF_FETCH_CHAR(b, pt - 1) == '\n';
525 }
526
527 DEFUN("bolp", Fbolp, 0, 1, 0,   /*
528 Return t if point is at the beginning of a line.
529 If BUFFER is nil, the current buffer is assumed.
530 */
531       (buffer))
532 {
533         struct buffer *b = decode_buffer(buffer, 1);
534         return beginning_of_line_p(b, BUF_PT(b)) ? Qt : Qnil;
535 }
536
537 DEFUN("eolp", Feolp, 0, 1, 0,   /*
538 Return t if point is at the end of a line.
539 `End of a line' includes point being at the end of the buffer.
540 If BUFFER is nil, the current buffer is assumed.
541 */
542       (buffer))
543 {
544         struct buffer *b = decode_buffer(buffer, 1);
545         return (BUF_PT(b) == BUF_ZV(b) || BUF_FETCH_CHAR(b, BUF_PT(b)) == '\n')
546             ? Qt : Qnil;
547 }
548
549 DEFUN("char-after", Fchar_after, 0, 2, 0,       /*
550 Return the character at position POS in BUFFER.
551 POS is an integer or a marker.
552 If POS is out of range, the value is nil.
553 if POS is nil, the value of point is assumed.
554 If BUFFER is nil, the current buffer is assumed.
555 */
556       (pos, buffer))
557 {
558         struct buffer *b = decode_buffer(buffer, 1);
559         Bufpos n = (NILP(pos) ? BUF_PT(b) :
560                     get_buffer_pos_char(b, pos, GB_NO_ERROR_IF_BAD));
561
562         if (n < 0 || n == BUF_ZV(b))
563                 return Qnil;
564         return make_char(BUF_FETCH_CHAR(b, n));
565 }
566
567 DEFUN("char-before", Fchar_before, 0, 2, 0,     /*
568 Return the character preceding position POS in BUFFER.
569 POS is an integer or a marker.
570 If POS is out of range, the value is nil.
571 if POS is nil, the value of point is assumed.
572 If BUFFER is nil, the current buffer is assumed.
573 */
574       (pos, buffer))
575 {
576         struct buffer *b = decode_buffer(buffer, 1);
577         Bufpos n = (NILP(pos) ? BUF_PT(b) :
578                     get_buffer_pos_char(b, pos, GB_NO_ERROR_IF_BAD));
579
580         n--;
581
582         if (n < BUF_BEGV(b))
583                 return Qnil;
584         return make_char(BUF_FETCH_CHAR(b, n));
585 }
586
587 #include <sys/stat.h>
588 #include <fcntl.h>
589 #include <errno.h>
590 #include <limits.h>
591
592 \f
593 DEFUN("temp-directory", Ftemp_directory, 0, 0, 0,       /*
594 Return the pathname to the directory to use for temporary files.
595 On MS Windows, this is obtained from the TEMP or TMP environment variables,
596 defaulting to / if they are both undefined.
597 On Unix it is obtained from TMPDIR, with /tmp as the default.
598 */
599       ())
600 {
601         char *tmpdir;
602         tmpdir = getenv("TMPDIR");
603         char path[5 /* strlen ("/tmp/") */  + 1 + _POSIX_PATH_MAX];
604         if (!tmpdir) {
605                 struct stat st;
606                 int myuid = getuid();
607
608                 strcpy(path, "/tmp/");
609                 strncat(path, user_login_name(NULL), _POSIX_PATH_MAX);
610                 path[sizeof(path)-1]=0;
611                 if (lstat(path, &st) < 0 && errno == ENOENT) {
612                         mkdir(path, 0700);      /* ignore retval -- checked next anyway. */
613                 }
614                 if (lstat(path, &st) == 0 && st.st_uid == (uid_t) myuid &&
615                     S_ISDIR(st.st_mode)) {
616                         tmpdir = path;
617                 } else {
618                         const char* home_env = getenv("HOME");
619                         if ( home_env ) {
620                                 xstrncpy(path, home_env, sizeof(path));
621                                 xstrncat(path, "/tmp/", sizeof(path));
622                                 if ( mkdir(path, 0700) >= 0 || errno == EEXIST ) {
623                                         int fd;
624                                         char warnpath[
625                                                 /* strlen(".created_by_sxemacs") */
626                                                 19 + _POSIX_PATH_MAX + 1];
627                                         xstrncpy(warnpath, path, sizeof(warnpath));
628
629                                         /* we already are reserved these 20 bytes... */
630                                         xstrncat(warnpath, ".created_by_sxemacs", 
631                                                  sizeof(warnpath)-1);
632                                         if ((fd = open(warnpath, O_WRONLY | O_CREAT,
633                                                        0644)) >= 0) {
634                                                 write(fd, "SXEmacs created this directory "
635                                                           "because /tmp/<yourname> "
636                                                           "was unavailable -- \nPlease check !\n",
637                                                       89);
638                                                 close(fd);
639                                         }
640                                 }
641                         }
642                         if (stat(path, &st) == 0 && st.st_uid == (uid_t) myuid
643                             && S_ISDIR(st.st_mode)) {
644                                 tmpdir = path;
645                         } else {
646                                 tmpdir = "/tmp";
647                         }
648                 }
649         }
650
651         return build_ext_string(tmpdir, Qfile_name);
652 }
653
654 DEFUN("user-login-name", Fuser_login_name, 0, 1, 0,     /*
655 Return the name under which the user logged in, as a string.
656 This is based on the effective uid, not the real uid.
657 Also, if the environment variable LOGNAME or USER is set,
658 that determines the value of this function.
659 If the optional argument UID is present, then environment variables are
660 ignored and this function returns the login name for that UID, or nil.
661 */
662       (uid))
663 {
664         char *returned_name;
665         uid_t local_uid;
666
667         if (!NILP(uid)) {
668                 CHECK_INT(uid);
669                 local_uid = XINT(uid);
670                 returned_name = user_login_name(&local_uid);
671         } else {
672                 returned_name = user_login_name(NULL);
673         }
674         /* #### - I believe this should return nil instead of "unknown" when pw==0
675            pw=0 is indicated by a null return from user_login_name
676          */
677         return returned_name ? build_string(returned_name) : Qnil;
678 }
679
680 /* This function may be called from other C routines when a
681    character string representation of the user_login_name is
682    needed but a Lisp Object is not.  The UID is passed by
683    reference.  If UID == NULL, then the USER name
684    for the user running XEmacs will be returned.  This
685    corresponds to a nil argument to Fuser_login_name.
686 */
687 char *user_login_name(uid_t * uid)
688 {
689         /* uid == NULL to return name of this user */
690         if (uid != NULL) {
691                 struct passwd *pw = getpwuid(*uid);
692                 return pw ? pw->pw_name : NULL;
693         } else {
694                 /* #### - when euid != uid, then LOGNAME and USER are leftovers from the
695                    old environment (I site observed behavior on sunos and linux), so the
696                    environment variables should be disregarded in that case.  --Stig */
697                 char *user_name = getenv("LOGNAME");
698                 if (!user_name)
699                         user_name = getenv(
700                                                   "USER"
701                             );
702                 if (user_name)
703                         return (user_name);
704                 else {
705                         struct passwd *pw = getpwuid(geteuid());
706                         return pw ? pw->pw_name : NULL;
707                 }
708         }
709 }
710
711 DEFUN("user-real-login-name", Fuser_real_login_name, 0, 0, 0,   /*
712 Return the name of the user's real uid, as a string.
713 This ignores the environment variables LOGNAME and USER, so it differs from
714 `user-login-name' when running under `su'.
715 */
716       ())
717 {
718         struct passwd *pw = getpwuid(getuid());
719         /* #### - I believe this should return nil instead of "unknown" when pw==0 */
720
721         Lisp_Object tem = build_string(pw ? pw->pw_name : "unknown");   /* no gettext */
722         return tem;
723 }
724
725 DEFUN("user-uid", Fuser_uid, 0, 0, 0,   /*
726 Return the effective uid of Emacs, as an integer.
727 */
728       ())
729 {
730         return make_int(geteuid());
731 }
732
733 DEFUN("user-real-uid", Fuser_real_uid, 0, 0, 0, /*
734 Return the real uid of Emacs, as an integer.
735 */
736       ())
737 {
738         return make_int(getuid());
739 }
740
741 DEFUN("user-full-name", Fuser_full_name, 0, 1, 0,       /*
742 Return the full name of the user logged in, as a string.
743 If the optional argument USER is given, then the full name for that
744 user is returned, or nil.  USER may be either a login name or a uid.
745
746 If USER is nil, and `user-full-name' contains a string, the
747 value of `user-full-name' is returned.
748 */
749       (user))
750 {
751         Lisp_Object user_name;
752         struct passwd *pw = NULL;
753         Lisp_Object tem;
754         char *p;
755         const char *q;
756
757         if (NILP(user) && STRINGP(Vuser_full_name))
758                 return Vuser_full_name;
759
760         user_name = (STRINGP(user) ? user : Fuser_login_name(user));
761         if (!NILP(user_name)) { /* nil when nonexistent UID passed as arg */
762                 const char *user_name_ext;
763
764                 /* Fuck me.  getpwnam() can call select() and (under IRIX at least)
765                    things get wedged if a SIGIO arrives during this time. */
766                 TO_EXTERNAL_FORMAT(LISP_STRING, user_name,
767                                    C_STRING_ALLOCA, user_name_ext, Qnative);
768                 slow_down_interrupts();
769                 pw = (struct passwd *)getpwnam(user_name_ext);
770                 speed_up_interrupts();
771         }
772
773         /* #### - Stig sez: this should return nil instead
774          * of "unknown" when pw==0 */
775         /* Ben sez: bad idea because it's likely to break something */
776 #ifndef AMPERSAND_FULL_NAME
777         p = pw ? USER_FULL_NAME : "unknown";    /* don't gettext */
778         q = strchr(p, ',');
779 #else
780         p = pw ? USER_FULL_NAME : "unknown";    /* don't gettext */
781         q = strchr(p, ',');
782 #endif
783         tem = ((!NILP(user) && !pw)
784                ? Qnil
785                : make_ext_string((Extbyte *) p, (q ? q - p : (int)strlen(p)),
786                                  Qnative));
787
788 #ifdef AMPERSAND_FULL_NAME
789         if (!NILP(tem)) {
790                 p = (char *)XSTRING_DATA(tem);
791                 q = strchr(p, '&');
792                 /* Substitute the login name for the &, upcasing the first character.  */
793                 if (q) {
794                         char *r =
795                             (char *)alloca(strlen(p) +
796                                            XSTRING_LENGTH(user_name) + 1);
797                         memcpy(r, p, q - p);
798                         r[q - p] = 0;
799                         strcat(r, (char *)XSTRING_DATA(user_name));
800                         /* #### current_buffer dependency! */
801                         r[q - p] = UPCASE(current_buffer, r[q - p]);
802                         strcat(r, q + 1);
803                         tem = build_string(r);
804                 }
805         }
806 #endif                          /* AMPERSAND_FULL_NAME */
807
808         return tem;
809 }
810
811 static Extbyte *cached_home_directory;
812
813 void uncache_home_directory(void)
814 {
815         cached_home_directory = NULL;   /* in some cases, this may cause the leaking
816                                            of a few bytes */
817 }
818
819 /* !!#### not Mule correct. */
820
821 /* Returns the home directory, in external format */
822 Extbyte *get_home_directory(void)
823 {
824         /* !!#### this is hopelessly bogus.  Rule #1: Do not make any assumptions
825            about what format an external string is in.  Could be Unicode, for all
826            we know, and then all the operations below are totally bogus.
827            Instead, convert all data to internal format *right* at the juncture
828            between XEmacs and the outside world, the very moment we first get
829            the data.  --ben */
830         int output_home_warning = 0;
831
832         if (cached_home_directory == NULL) {
833                 if ((cached_home_directory =
834                      (Extbyte *) getenv("HOME")) == NULL) {
835                         /*
836                          * Unix, typically.
837                          * Using "/" isn't quite right, but what should we do?
838                          * We probably should try to extract pw_dir from /etc/passwd,
839                          * before falling back to this.
840                          */
841                         cached_home_directory = (Extbyte *) "/";
842                         output_home_warning = 1;
843                 }
844                 if (initialized && output_home_warning) {
845                         warn_when_safe(Quser_files_and_directories, Qwarning,
846                                        "\n"
847                                        "        SXEmacs was unable to determine a good value for the user's $HOME\n"
848                                        "        directory, and will be using the value:\n"
849                                        "                %s\n"
850                                        "        This is probably incorrect.",
851                                        cached_home_directory);
852                 }
853         }
854         return cached_home_directory;
855 }
856
857 DEFUN("user-home-directory", Fuser_home_directory, 0, 0, 0,     /*
858 Return the user's home directory, as a string.
859 */
860       ())
861 {
862         Extbyte *path = get_home_directory();
863
864         return path == NULL ? Qnil :
865             Fexpand_file_name(Fsubstitute_in_file_name
866                               (build_ext_string((char *)path, Qfile_name)),
867                               Qnil);
868 }
869
870 DEFUN("system-name", Fsystem_name, 0, 0, 0,     /*
871 Return the name of the machine you are running on, as a string.
872 */
873       ())
874 {
875         return Fcopy_sequence(Vsystem_name);
876 }
877
878 DEFUN("emacs-pid", Femacs_pid, 0, 0, 0, /*
879 Return the process ID of Emacs, as an integer.
880 */
881       ())
882 {
883         return make_int(getpid());
884 }
885
886 DEFUN("current-time", Fcurrent_time, 0, 0, 0,   /*
887 Return the current time, as the number of seconds since 1970-01-01 00:00:00.