OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / sys / unix / unixmain.c
1 /* NetHack 3.6  unixmain.c      $NHDT-Date: 1432512788 2015/05/25 00:13:08 $  $NHDT-Branch: master $:$NHDT-Revision: 1.52 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2011. */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 /* main.c - Unix NetHack */
7
8 #include "hack.h"
9 #include "dlb.h"
10
11 #include <ctype.h>
12 #include <sys/stat.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #ifndef O_RDONLY
16 #include <fcntl.h>
17 #endif
18
19 #ifdef XI18N
20 #include <X11/Xlocale.h>
21 #endif
22
23 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
24 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
25 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
26 extern struct passwd *FDECL(getpwuid, (uid_t));
27 #else
28 extern struct passwd *FDECL(getpwuid, (int));
29 #endif
30 #endif
31 #endif
32 extern struct passwd *FDECL(getpwnam, (const char *));
33 #ifdef CHDIR
34 static void FDECL(chdirx, (const char *, BOOLEAN_P));
35 #endif /* CHDIR */
36 static boolean NDECL(whoami);
37 static void FDECL(process_options, (int, char **));
38
39 #ifdef _M_UNIX
40 extern void NDECL(check_sco_console);
41 extern void NDECL(init_sco_cons);
42 #endif
43 #ifdef __linux__
44 extern void NDECL(check_linux_console);
45 extern void NDECL(init_linux_cons);
46 #endif
47
48 static void NDECL(wd_message);
49 static boolean wiz_error_flag = FALSE;
50 static struct passwd *NDECL(get_unix_pw);
51
52 int
53 main(argc, argv)
54 int argc;
55 char *argv[];
56 {
57     register int fd;
58 #ifdef CHDIR
59     register char *dir;
60 #endif
61     boolean exact_username;
62     boolean resuming = FALSE; /* assume new game */
63     boolean plsel_once = FALSE;
64
65     sys_early_init();
66
67 #ifdef XI18N
68     setlocale(LC_ALL, "");
69 #endif
70 #if defined(__APPLE__)
71     {
72 /* special hack to change working directory to a resource fork when
73    running from finder --sam */
74 #define MAC_PATH_VALUE ".app/Contents/MacOS/"
75         char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp;
76         int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0;
77         getcwd(mac_cwd, 1024);
78         if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) {
79             if ((mac_exe = strrchr(mac_exe, '/')))
80                 mac_exe++;
81             else
82                 mac_exe = argv[0];
83             mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE);
84             if (mac_tmp_len <= arg0_len) {
85                 mac_tmp = malloc(mac_tmp_len + 1);
86                 sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe);
87                 if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) {
88                     mac_lhs_len =
89                         (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5;
90                     if (mac_lhs_len > mac_tmp_len - 1)
91                         mac_tmp = realloc(mac_tmp, mac_lhs_len);
92                     strncpy(mac_tmp, argv[0], mac_lhs_len);
93                     mac_tmp[mac_lhs_len] = '\0';
94                     chdir(mac_tmp);
95                 }
96                 free(mac_tmp);
97             }
98         }
99     }
100 #endif
101
102     hname = argv[0];
103     hackpid = getpid();
104     (void) umask(0777 & ~FCMASK);
105
106     choose_windows(DEFAULT_WINDOW_SYS);
107
108 #ifdef CHDIR /* otherwise no chdir() */
109     /*
110      * See if we must change directory to the playground.
111      * (Perhaps hack runs suid and playground is inaccessible
112      *  for the player.)
113      * The environment variable HACKDIR is overridden by a
114      *  -d command line option (must be the first option given).
115      */
116     dir = nh_getenv("NETHACKDIR");
117     if (!dir)
118         dir = nh_getenv("HACKDIR");
119
120     if (argc > 1) {
121         if (argcheck(argc, argv, ARG_VERSION))
122             exit(EXIT_SUCCESS);
123
124         if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
125             /* avoid matching "-dec" for DECgraphics; since the man page
126              * says -d directory, hope nobody's using -desomething_else
127              */
128             argc--;
129             argv++;
130             dir = argv[0] + 2;
131             if (*dir == '=' || *dir == ':')
132                 dir++;
133             if (!*dir && argc > 1) {
134                 argc--;
135                 argv++;
136                 dir = argv[0];
137             }
138             if (!*dir)
139                 error("Flag -d must be followed by a directory name.");
140         }
141     }
142 #endif /* CHDIR */
143
144     if (argc > 1) {
145         /*
146          * Now we know the directory containing 'record' and
147          * may do a prscore().  Exclude `-style' - it's a Qt option.
148          */
149         if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
150 #ifdef CHDIR
151             chdirx(dir, 0);
152 #endif
153 #ifdef SYSCF
154             initoptions();
155 #endif
156 #ifdef PANICTRACE
157             ARGV0 = hname; /* save for possible stack trace */
158 #ifndef NO_SIGNAL
159             panictrace_setsignals(TRUE);
160 #endif
161 #endif
162 #if 0 /*JP*/
163             prscore(argc, argv);
164 #else
165             setkcode('I');
166             initoptions();
167             prscore(argc, argv);
168             jputchar('\0'); /* reset */
169 #endif
170             /* FIXME: shouldn't this be using nh_terminate() to free
171                up any memory allocated by initoptions() */
172             exit(EXIT_SUCCESS);
173         }
174     } /* argc > 1 */
175
176 /*
177  * Change directories before we initialize the window system so
178  * we can find the tile file.
179  */
180 #ifdef CHDIR
181     chdirx(dir, 1);
182 #endif
183
184 #ifdef _M_UNIX
185     check_sco_console();
186 #endif
187 #ifdef __linux__
188     check_linux_console();
189 #endif
190 #if 1 /*JP*/
191     /* Line like "OPTIONS=name:foo-@" may exist in config file.
192      * In this case, need to select random class,
193      * so must call setrandom() before initoptions().
194      */
195     setrandom();
196 #endif
197     initoptions();
198 #ifdef PANICTRACE
199     ARGV0 = hname; /* save for possible stack trace */
200 #ifndef NO_SIGNAL
201     panictrace_setsignals(TRUE);
202 #endif
203 #endif
204     exact_username = whoami();
205
206     /*
207      * It seems you really want to play.
208      */
209     u.uhp = 1; /* prevent RIP on early quits */
210     program_state.preserve_locks = 1;
211 #ifndef NO_SIGNAL
212     sethanguphandler((SIG_RET_TYPE) hangup);
213 #endif
214
215     process_options(argc, argv); /* command line options */
216 #ifdef WINCHAIN
217     commit_windowchain();
218 #endif
219     init_nhwindows(&argc, argv); /* now we can set up window system */
220 #ifdef _M_UNIX
221     init_sco_cons();
222 #endif
223 #ifdef __linux__
224     init_linux_cons();
225 #endif
226
227 #ifdef DEF_PAGER
228     if (!(catmore = nh_getenv("HACKPAGER"))
229         && !(catmore = nh_getenv("PAGER")))
230         catmore = DEF_PAGER;
231 #endif
232 #ifdef MAIL
233     getmailstatus();
234 #endif
235
236     /* wizard mode access is deferred until here */
237     set_playmode(); /* sets plname to "wizard" for wizard mode */
238     if (exact_username) {
239         /*
240          * FIXME: this no longer works, ever since 3.3.0
241          * when plnamesuffix() was changed to find
242          * Name-Role-Race-Gender-Alignment.  It removes
243          * all dashes rather than just the last one,
244          * regardless of whether whatever follows each
245          * dash matches role, race, gender, or alignment.
246          */
247         /* guard against user names with hyphens in them */
248         int len = (int) strlen(plname);
249         /* append the current role, if any, so that last dash is ours */
250         if (++len < (int) sizeof plname)
251             (void) strncat(strcat(plname, "-"), pl_character,
252                            sizeof plname - len - 1);
253     }
254     /* strip role,race,&c suffix; calls askname() if plname[] is empty
255        or holds a generic user name like "player" or "games" */
256     plnamesuffix();
257
258     if (wizard) {
259         /* use character name rather than lock letter for file names */
260         locknum = 0;
261     } else {
262         /* suppress interrupts while processing lock file */
263         (void) signal(SIGQUIT, SIG_IGN);
264         (void) signal(SIGINT, SIG_IGN);
265     }
266
267     dlb_init(); /* must be before newgame() */
268
269     /*
270      * Initialize the vision system.  This must be before mklev() on a
271      * new game or before a level restore on a saved game.
272      */
273     vision_init();
274
275     display_gamewindows();
276
277     /*
278      * First, try to find and restore a save file for specified character.
279      * We'll return here if new game player_selection() renames the hero.
280      */
281 attempt_restore:
282
283     /*
284      * getlock() complains and quits if there is already a game
285      * in progress for current character name (when locknum == 0)
286      * or if there are too many active games (when locknum > 0).
287      * When proceeding, it creates an empty <lockname>.0 file to
288      * designate the current game.
289      * getlock() constructs <lockname> based on the character
290      * name (for !locknum) or on first available of alock, block,
291      * clock, &c not currently in use in the playground directory
292      * (for locknum > 0).
293      */
294     if (*plname) {
295         getlock();
296         program_state.preserve_locks = 0; /* after getlock() */
297     }
298
299     if (*plname && (fd = restore_saved_game()) >= 0) {
300         const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
301
302         (void) chmod(fq_save, 0); /* disallow parallel restores */
303 #ifndef NO_SIGNAL
304         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
305 #endif
306 #ifdef NEWS
307         if (iflags.news) {
308             display_file(NEWS, FALSE);
309             iflags.news = FALSE; /* in case dorecover() fails */
310         }
311 #endif
312 /*JP
313         pline("Restoring save file...");
314 */
315         pline("\83Z\81[\83u\83t\83@\83C\83\8b\82ð\95\9c\8c³\92\86\81D\81D\81D");
316         mark_synch(); /* flush output */
317         if (dorecover(fd)) {
318             resuming = TRUE; /* not starting new game */
319             wd_message();
320             if (discover || wizard) {
321                 /* this seems like a candidate for paranoid_confirmation... */
322 /*JP
323                 if (yn("Do you want to keep the save file?") == 'n') {
324 */
325                 if (yn("\83Z\81[\83u\83t\83@\83C\83\8b\82ð\8ec\82µ\82Ä\82¨\82«\82Ü\82·\82©\81H") == 'n') {
326                     (void) delete_savefile();
327                 } else {
328                     (void) chmod(fq_save, FCMASK); /* back to readable */
329                     nh_compress(fq_save);
330                 }
331             }
332         }
333     }
334
335     if (!resuming) {
336         boolean neednewlock = (!*plname);
337         /* new game:  start by choosing role, race, etc;
338            player might change the hero's name while doing that,
339            in which case we try to restore under the new name
340            and skip selection this time if that didn't succeed */
341         if (!iflags.renameinprogress || iflags.defer_plname || neednewlock) {
342             if (!plsel_once)
343                 player_selection();
344             plsel_once = TRUE;
345             if (neednewlock && *plname)
346                 goto attempt_restore;
347             if (iflags.renameinprogress) {
348                 /* player has renamed the hero while selecting role;
349                    if locking alphabetically, the existing lock file
350                    can still be used; otherwise, discard current one
351                    and create another for the new character name */
352                 if (!locknum) {
353                     delete_levelfile(0); /* remove empty lock file */
354                     getlock();
355                 }
356                 goto attempt_restore;
357             }
358         }
359         newgame();
360         wd_message();
361     }
362
363     /* moveloop() never returns but isn't flagged NORETURN */
364     moveloop(resuming);
365
366     exit(EXIT_SUCCESS);
367     /*NOTREACHED*/
368     return 0;
369 }
370
371 static void
372 process_options(argc, argv)
373 int argc;
374 char *argv[];
375 {
376     int i, l;
377
378     /*
379      * Process options.
380      */
381     while (argc > 1 && argv[1][0] == '-') {
382         argv++;
383         argc--;
384         l = (int) strlen(*argv);
385         /* must supply at least 4 chars to match "-XXXgraphics" */
386         if (l < 4)
387             l = 4;
388
389         switch (argv[0][1]) {
390         case 'D':
391         case 'd':
392             if ((argv[0][1] == 'D' && !argv[0][2])
393                 || !strcmpi(*argv, "-debug")) {
394                 wizard = TRUE, discover = FALSE;
395             } else if (!strncmpi(*argv, "-DECgraphics", l)) {
396                 load_symset("DECGraphics", PRIMARY);
397                 switch_symbols(TRUE);
398             } else {
399                 raw_printf("Unknown option: %s", *argv);
400             }
401             break;
402         case 'X':
403
404             discover = TRUE, wizard = FALSE;
405             break;
406 #ifdef NEWS
407         case 'n':
408             iflags.news = FALSE;
409             break;
410 #endif
411         case 'u':
412             if (argv[0][2]) {
413 #if 0 /*JP*/
414                 (void) strncpy(plname, argv[0] + 2, sizeof plname - 1);
415 #else
416                 (void) strncpy(plname, str2ic(argv[0] + 2), sizeof(plname) - 1);
417 #endif
418             } else if (argc > 1) {
419                 argc--;
420                 argv++;
421 #if 0 /*JP*/
422                 (void) strncpy(plname, argv[0], sizeof plname - 1);
423 #else
424                 (void) strncpy(plname, str2ic(argv[0]), sizeof(plname) - 1);
425 #endif
426             } else {
427                 raw_print("Player name expected after -u");
428             }
429             break;
430         case 'I':
431         case 'i':
432             if (!strncmpi(*argv, "-IBMgraphics", l)) {
433                 load_symset("IBMGraphics", PRIMARY);
434                 load_symset("RogueIBM", ROGUESET);
435                 switch_symbols(TRUE);
436             } else {
437                 raw_printf("Unknown option: %s", *argv);
438             }
439             break;
440         case 'p': /* profession (role) */
441             if (argv[0][2]) {
442                 if ((i = str2role(&argv[0][2])) >= 0)
443                     flags.initrole = i;
444             } else if (argc > 1) {
445                 argc--;
446                 argv++;
447                 if ((i = str2role(argv[0])) >= 0)
448                     flags.initrole = i;
449             }
450             break;
451         case 'r': /* race */
452             if (argv[0][2]) {
453                 if ((i = str2race(&argv[0][2])) >= 0)
454                     flags.initrace = i;
455             } else if (argc > 1) {
456                 argc--;
457                 argv++;
458                 if ((i = str2race(argv[0])) >= 0)
459                     flags.initrace = i;
460             }
461             break;
462         case 'w': /* windowtype */
463             config_error_init(FALSE, "command line", FALSE);
464             choose_windows(&argv[0][2]);
465             config_error_done();
466             break;
467         case '@':
468             flags.randomall = 1;
469             break;
470         default:
471             if ((i = str2role(&argv[0][1])) >= 0) {
472                 flags.initrole = i;
473                 break;
474             }
475             /* else raw_printf("Unknown option: %s", *argv); */
476         }
477     }
478
479 #ifdef SYSCF
480     if (argc > 1)
481         raw_printf("MAXPLAYERS are set in sysconf file.\n");
482 #else
483     /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */
484     if (argc > 1)
485         locknum = atoi(argv[1]);
486 #endif
487 #ifdef MAX_NR_OF_PLAYERS
488     /* limit to compile-time limit */
489     if (!locknum || locknum > MAX_NR_OF_PLAYERS)
490         locknum = MAX_NR_OF_PLAYERS;
491 #endif
492 #ifdef SYSCF
493     /* let syscf override compile-time limit */
494     if (!locknum || (sysopt.maxplayers && locknum > sysopt.maxplayers))
495         locknum = sysopt.maxplayers;
496 #endif
497 }
498
499 #ifdef CHDIR
500 static void
501 chdirx(dir, wr)
502 const char *dir;
503 boolean wr;
504 {
505     if (dir /* User specified directory? */
506 #ifdef HACKDIR
507         && strcmp(dir, HACKDIR) /* and not the default? */
508 #endif
509         ) {
510 #ifdef SECURE
511 #pragma GCC diagnostic push
512 #pragma GCC diagnostic ignored "-Wunused-result"
513         (void) setgid(getgid());
514         (void) setuid(getuid()); /* Ron Wessels */
515 #pragma GCC diagnostic pop
516 #endif
517     } else {
518         /* non-default data files is a sign that scores may not be
519          * compatible, or perhaps that a binary not fitting this
520          * system's layout is being used.
521          */
522 #ifdef VAR_PLAYGROUND
523         int len = strlen(VAR_PLAYGROUND);
524
525         fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2);
526         Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
527         if (fqn_prefix[SCOREPREFIX][len - 1] != '/') {
528             fqn_prefix[SCOREPREFIX][len] = '/';
529             fqn_prefix[SCOREPREFIX][len + 1] = '\0';
530         }
531 #endif
532     }
533
534 #ifdef HACKDIR
535     if (dir == (const char *) 0)
536         dir = HACKDIR;
537 #endif
538
539     if (dir && chdir(dir) < 0) {
540         perror(dir);
541         error("Cannot chdir to %s.", dir);
542     }
543
544     /* warn the player if we can't write the record file
545      * perhaps we should also test whether . is writable
546      * unfortunately the access system-call is worthless.
547      */
548     if (wr) {
549 #ifdef VAR_PLAYGROUND
550         fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
551         fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
552         fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
553         fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
554         fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
555 #endif
556         check_recordfile(dir);
557     }
558 }
559 #endif /* CHDIR */
560
561 /* returns True iff we set plname[] to username which contains a hyphen */
562 static boolean
563 whoami()
564 {
565     /*
566      * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
567      *                  2. Use $USER or $LOGNAME        (if 1. fails)
568      *                  3. Use getlogin()               (if 2. fails)
569      * The resulting name is overridden by command line options.
570      * If everything fails, or if the resulting name is some generic
571      * account like "games", "play", "player", "hack" then eventually
572      * we'll ask him.
573      * Note that we trust the user here; it is possible to play under
574      * somebody else's name.
575      */
576     if (!*plname) {
577         register const char *s;
578
579         s = nh_getenv("USER");
580         if (!s || !*s)
581             s = nh_getenv("LOGNAME");
582         if (!s || !*s)
583             s = getlogin();
584
585         if (s && *s) {
586             (void) strncpy(plname, s, sizeof plname - 1);
587             if (index(plname, '-'))
588                 return TRUE;
589         }
590     }
591     return FALSE;
592 }
593
594 void
595 sethanguphandler(handler)
596 void FDECL((*handler), (int));
597 {
598 #ifdef SA_RESTART
599     /* don't want reads to restart.  If SA_RESTART is defined, we know
600      * sigaction exists and can be used to ensure reads won't restart.
601      * If it's not defined, assume reads do not restart.  If reads restart
602      * and a signal occurs, the game won't do anything until the read
603      * succeeds (or the stream returns EOF, which might not happen if
604      * reading from, say, a window manager). */
605     struct sigaction sact;
606
607     (void) memset((genericptr_t) &sact, 0, sizeof sact);
608     sact.sa_handler = (SIG_RET_TYPE) handler;
609     (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
610 #ifdef SIGXCPU
611     (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
612 #endif
613 #else /* !SA_RESTART */
614     (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
615 #ifdef SIGXCPU
616     (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
617 #endif
618 #endif /* ?SA_RESTART */
619 }
620
621 #ifdef PORT_HELP
622 void
623 port_help()
624 {
625     /*
626      * Display unix-specific help.   Just show contents of the helpfile
627      * named by PORT_HELP.
628      */
629     display_file(PORT_HELP, TRUE);
630 }
631 #endif
632
633 /* validate wizard mode if player has requested access to it */
634 boolean
635 authorize_wizard_mode()
636 {
637     struct passwd *pw = get_unix_pw();
638
639     if (pw && sysopt.wizards && sysopt.wizards[0]) {
640         if (check_user_string(sysopt.wizards))
641             return TRUE;
642     }
643     wiz_error_flag = TRUE; /* not being allowed into wizard mode */
644     return FALSE;
645 }
646
647 static void
648 wd_message()
649 {
650     if (wiz_error_flag) {
651         if (sysopt.wizards && sysopt.wizards[0]) {
652             char *tmp = build_english_list(sysopt.wizards);
653 #if 0 /*JP*/
654             pline("Only user%s %s may access debug (wizard) mode.",
655                   index(sysopt.wizards, ' ') ? "s" : "", tmp);
656 #else
657             pline("\81u%s\81v\82Ì\82Ý\82ª\83f\83o\83b\83O(\83E\83C\83U\81[\83h)\83\82\81[\83h\82ð\8eg\97p\82Å\82«\82é\81D",
658                   tmp);
659 #endif
660             free(tmp);
661         } else
662 /*JP
663             pline("Entering explore/discovery mode instead.");
664 */
665             pline("\82©\82í\82è\82É\94­\8c©\83\82\81[\83h\82Ö\88Ú\8ds\82·\82é\81D");
666         wizard = 0, discover = 1; /* (paranoia) */
667     } else if (discover)
668 /*JP
669         You("are in non-scoring explore/discovery mode.");
670 */
671         You("\83X\83R\83A\82Ì\8dÚ\82ç\82È\82¢\94­\8c©\83\82\81[\83h\82Å\8bN\93®\82µ\82½\81D");
672 }
673
674 /*
675  * Add a slash to any name not ending in /. There must
676  * be room for the /
677  */
678 void
679 append_slash(name)
680 char *name;
681 {
682     char *ptr;
683
684     if (!*name)
685         return;
686     ptr = name + (strlen(name) - 1);
687     if (*ptr != '/') {
688         *++ptr = '/';
689         *++ptr = '\0';
690     }
691     return;
692 }
693
694 boolean
695 check_user_string(optstr)
696 char *optstr;
697 {
698     struct passwd *pw = get_unix_pw();
699     int pwlen;
700     char *eop, *w;
701     char *pwname;
702
703     if (optstr[0] == '*')
704         return TRUE; /* allow any user */
705     if (!pw)
706         return FALSE;
707     if (sysopt.check_plname)
708         pwname = plname;
709     else
710         pwname = pw->pw_name;
711     pwlen = strlen(pwname);
712     eop = eos(optstr);
713     w = optstr;
714     while (w + pwlen <= eop) {
715         if (!*w)
716             break;
717         if (isspace(*w)) {
718             w++;
719             continue;
720         }
721         if (!strncmp(w, pwname, pwlen)) {
722             if (!w[pwlen] || isspace(w[pwlen]))
723                 return TRUE;
724         }
725         while (*w && !isspace(*w))
726             w++;
727     }
728     return FALSE;
729 }
730
731 static struct passwd *
732 get_unix_pw()
733 {
734     char *user;
735     unsigned uid;
736     static struct passwd *pw = (struct passwd *) 0;
737
738     if (pw)
739         return pw; /* cache answer */
740
741     uid = (unsigned) getuid();
742     user = getlogin();
743     if (user) {
744         pw = getpwnam(user);
745         if (pw && ((unsigned) pw->pw_uid != uid))
746             pw = 0;
747     }
748     if (pw == 0) {
749         user = nh_getenv("USER");
750         if (user) {
751             pw = getpwnam(user);
752             if (pw && ((unsigned) pw->pw_uid != uid))
753                 pw = 0;
754         }
755         if (pw == 0) {
756             pw = getpwuid(uid);
757         }
758     }
759     return pw;
760 }
761
762 char *
763 get_login_name()
764 {
765     static char buf[BUFSZ];
766     struct passwd *pw = get_unix_pw();
767
768     buf[0] = '\0';
769
770     if (pw)
771         (void)strcpy(buf, pw->pw_name);
772
773     return buf;
774 }
775
776 #ifdef __APPLE__
777 extern int errno;
778
779 void
780 port_insert_pastebuf(buf)
781 char *buf;
782 {
783     /* This should be replaced when there is a Cocoa port. */
784     const char *errfmt;
785     size_t len;
786     FILE *PB = popen("/usr/bin/pbcopy","w");
787     if(!PB){
788         errfmt = "Unable to start pbcopy (%d)\n";
789         goto error;
790     }
791
792     len = strlen(buf);
793     /* Remove the trailing \n, carefully. */
794     if(buf[len-1] == '\n') len--;
795
796     /* XXX Sorry, I'm too lazy to write a loop for output this short. */
797     if(len!=fwrite(buf,1,len,PB)){
798         errfmt = "Error sending data to pbcopy (%d)\n";
799         goto error;
800     }
801
802     if(pclose(PB)!=-1){
803         return;
804     }
805     errfmt = "Error finishing pbcopy (%d)\n";
806
807 error:
808     raw_printf(errfmt,strerror(errno));
809 }
810 #endif
811
812 /*unixmain.c*/