OSDN Git Service

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