OpenSSL build fixes from Nelson
[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                                 strncpy(path, home_env, sizeof(path)-1);
621                                 strncat(path, "/tmp/", sizeof(path)-1);
622                                 if (stat(path, &st) < 0 && errno == ENOENT) {
623                                         int fd;
624                                         char warnpath[ 
625                                                 /* strlen(".created_by_sxemacs") */ 
626                                                 19 + _POSIX_PATH_MAX + 1];
627                                         mkdir(path, 0700);      /* ignore retvals */
628                                         strncpy(warnpath, path, _POSIX_PATH_MAX);
629                                         warnpath[sizeof(warnpath)-1]=0;
630                                         
631                                         /* we already are reserved these 20 bytes... */
632                                         strcat(warnpath, ".created_by_sxemacs");
633                                         if ((fd = open(warnpath, O_WRONLY | O_CREAT,
634                                                        0644)) > 0) {
635                                                 write(fd, "SXEmacs created this directory "
636                                                           "because /tmp/<yourname> "
637                                                           "was unavailable -- \nPlease check !\n",  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)