[BACK]Return to dgamelaunch.c CVS log [TXT][DIR] Up to [contributed] / dgamelaunch-openbsd

Annotation of dgamelaunch-openbsd/dgamelaunch.c, Revision 1.1

1.1     ! rubenllo    1: /* dgamelaunch.c
        !             2:  *
        !             3:  * (c)2001-4 M. Drew Streib <dtype@dtype.org>
        !             4:  * also parts (c) 2003-4 Joshua Kwan <joshk@triplehelix.org>,
        !             5:  * Brett Carrington <brettcar@segvio.org>,
        !             6:  * Jilles Tjoelker <jilles@stack.nl>
        !             7:  *
        !             8:  * This program 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 2 of the License, or
        !            11:  * (at your option) any later version.
        !            12:  *
        !            13:  * This program 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, write to the Free Software
        !            20:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
        !            21:  */
        !            22:
        !            23: /*
        !            24:  * See this program in action at http://alt.org/nethack/
        !            25:  *
        !            26:  * This is a little wrapper for nethack (and soon other programs) that
        !            27:  * will allow them to be run from a telnetd session, chroot, shed privs,
        !            28:  * make a simple login, then play the game.
        !            29:  */
        !            30:
        !            31: #define _GNU_SOURCE
        !            32:
        !            33: #include "dgamelaunch.h"
        !            34: #include "config.h"
        !            35: #include "ttyplay.h"
        !            36: #include "ttyrec.h"
        !            37:
        !            38: /* a request from the author: please leave some remnance of
        !            39:  * 'based on dgamelaunch version xxx' in any derivative works, or
        !            40:  * even keep the line the same altogether. I'm probably happy
        !            41:  * to make any changes you need. */
        !            42:
        !            43: /* ************************************************************* */
        !            44:
        !            45: /* program stuff */
        !            46: #include <sys/types.h>
        !            47: #include <sys/time.h>
        !            48: #include <sys/wait.h>
        !            49: #include <sys/ioctl.h>          /* ttyrec */
        !            50: #include <sys/stat.h>
        !            51: #ifdef USE_RLIMIT
        !            52: #include <sys/resource.h>
        !            53: #endif
        !            54:
        !            55: #ifdef USE_SHMEM
        !            56: #include <sys/ipc.h>
        !            57: #include <sys/shm.h>
        !            58: #endif
        !            59:
        !            60: #include <libgen.h>
        !            61: #include <stdlib.h>
        !            62: #include <curses.h>
        !            63: #include <locale.h>
        !            64:
        !            65: #ifdef USE_SQLITE3
        !            66: # include <sqlite3.h>
        !            67: #endif
        !            68:
        !            69: #if defined(__FreeBSD__)
        !            70: # include <libutil.h>
        !            71: #elif defined(__NetBSD__)
        !            72: # include <util.h>
        !            73: #elif defined(__APPLE__)
        !            74: # include <unistd.h>
        !            75: #else
        !            76: # include <crypt.h>
        !            77: #endif
        !            78:
        !            79: #ifdef __linux__
        !            80: # include <pty.h>
        !            81: #endif
        !            82:
        !            83: #include <fcntl.h>
        !            84: #include <pwd.h>
        !            85: #include <grp.h>
        !            86: #include <time.h>
        !            87: #include <errno.h>
        !            88: #include <dirent.h>
        !            89: #include <string.h>
        !            90: #include <signal.h>
        !            91: #include <assert.h>
        !            92: #include <ctype.h>
        !            93: #include <unistd.h>
        !            94: #include <termios.h>
        !            95:
        !            96: extern FILE* yyin;
        !            97: extern int yyparse ();
        !            98:
        !            99: /* global variables */
        !           100:
        !           101: char * __progname;
        !           102: int  g_idle_alarm_enabled = 0;
        !           103: int  showplayers = 0;
        !           104: int  initplayer = 0;
        !           105: void (*g_chain_winch)(int);
        !           106:
        !           107: #ifndef USE_SQLITE3
        !           108: int f_num = 0;
        !           109: struct dg_user **users = NULL;
        !           110: #endif
        !           111: struct dg_user *me = NULL;
        !           112: struct dg_banner banner;
        !           113:
        !           114: static struct dg_watchcols default_watchcols[] = {
        !           115:     {SORTMODE_NONE, SORTMODE_NONE,        1, "", "%s)"},
        !           116:     {SORTMODE_USERNAME, SORTMODE_USERNAME,    4, "Username", "%-15s"},
        !           117:     {SORTMODE_GAMENUM, SORTMODE_GAMENUM,    21, "Game", "%-5s"},
        !           118:     {SORTMODE_WINDOWSIZE, SORTMODE_WINDOWSIZE, 28, " Size", "%s"},
        !           119:     {SORTMODE_STARTTIME, SORTMODE_STARTTIME,  37, "Start date & time", "%s"},
        !           120:     {SORTMODE_IDLETIME, SORTMODE_IDLETIME,   58, "Idle time", "%-10s"},
        !           121: #ifdef USE_SHMEM
        !           122:     {SORTMODE_WATCHERS, SORTMODE_WATCHERS,   70, "Watchers", "%s"},
        !           123: #endif
        !           124: };
        !           125:
        !           126: int color_remap[16] = {
        !           127:     COLOR_PAIR(9) | A_NORMAL,
        !           128:     COLOR_PAIR(COLOR_BLUE) | A_NORMAL,
        !           129:     COLOR_PAIR(COLOR_GREEN) | A_NORMAL,
        !           130:     COLOR_PAIR(COLOR_CYAN) | A_NORMAL,
        !           131:     COLOR_PAIR(COLOR_RED) | A_NORMAL,
        !           132:     COLOR_PAIR(COLOR_MAGENTA) | A_NORMAL,
        !           133:     COLOR_PAIR(COLOR_YELLOW) | A_NORMAL,
        !           134:     COLOR_PAIR(COLOR_BLACK) | A_NORMAL,
        !           135:     COLOR_PAIR(10) | A_BOLD,
        !           136:     COLOR_PAIR(COLOR_BLUE) | A_BOLD,
        !           137:     COLOR_PAIR(COLOR_GREEN) | A_BOLD,
        !           138:     COLOR_PAIR(COLOR_CYAN) | A_BOLD,
        !           139:     COLOR_PAIR(COLOR_RED) | A_BOLD,
        !           140:     COLOR_PAIR(COLOR_MAGENTA) | A_BOLD,
        !           141:     COLOR_PAIR(COLOR_YELLOW) | A_BOLD,
        !           142:     COLOR_PAIR(COLOR_WHITE) | A_BOLD,
        !           143: };
        !           144:
        !           145: static struct dg_watchcols *default_watchcols_list[DGL_MAXWATCHCOLS + 1];
        !           146:
        !           147: struct dg_user *
        !           148: cpy_me(struct dg_user *me)
        !           149: {
        !           150:     struct dg_user *tmp = malloc(sizeof(struct dg_user));
        !           151:
        !           152:     if (tmp && me) {
        !           153: #ifdef USE_SQLITE3
        !           154:        tmp->id = me->id;
        !           155: #endif
        !           156:        if (me->username) tmp->username = strdup(me->username);
        !           157:        if (me->email)    tmp->email    = strdup(me->email);
        !           158:        if (me->env)      tmp->env      = strdup(me->env);
        !           159:        if (me->password) tmp->password = strdup(me->password);
        !           160:        tmp->flags = me->flags;
        !           161:     }
        !           162:     return tmp;
        !           163: }
        !           164:
        !           165: #ifndef HAVE_SETENV
        !           166: int
        !           167: mysetenv (const char* name, const char* value, int overwrite)
        !           168: {
        !           169:   int retval;
        !           170:   char *buf = NULL;
        !           171:
        !           172:   if (getenv(name) == NULL || overwrite)
        !           173:   {
        !           174:     size_t len = strlen(name) + 1 + strlen(value) + 1; /* NAME=VALUE\0 */
        !           175:     buf = malloc(len);
        !           176:     snprintf(buf, len, "%s=%s", name, value);
        !           177:     retval = putenv(buf);
        !           178:   }
        !           179:   else
        !           180:     retval = -1;
        !           181:
        !           182:   return retval;
        !           183: }
        !           184: #else /* use native setenv */
        !           185: # define mysetenv setenv
        !           186: #endif
        !           187:
        !           188: /* ************************************************************* */
        !           189: /* for ttyrec */
        !           190:
        !           191: void
        !           192: ttyrec_getpty ()
        !           193: {
        !           194: #ifdef HAVE_OPENPTY
        !           195:     if (openpty (&master, &slave, NULL, NULL, NULL) == -1) {
        !           196:        debug_write("cannot openpty");
        !           197:        graceful_exit (61);
        !           198:     }
        !           199: #else
        !           200:     if ((master = open ("/dev/ptmx", O_RDWR)) < 0) {
        !           201:        debug_write("cannot open /dev/ptmx");
        !           202:        graceful_exit (62);
        !           203:     }
        !           204:   grantpt (master);
        !           205:   unlockpt (master);
        !           206:   if ((slave = open ((const char *) ptsname (master), O_RDWR)) < 0)
        !           207:     {
        !           208:        debug_write("cannot open master ptsname");
        !           209:       graceful_exit (65);
        !           210:     }
        !           211: #endif
        !           212:   ioctl (slave, TIOCSWINSZ, (char *) &win);
        !           213:   tcsetattr(slave, TCSANOW, &tt);
        !           214: }
        !           215:
        !           216: /* ************************************************************* */
        !           217:
        !           218: static int dgl_signal_blocked = 0;
        !           219: static sigset_t dgl_signal_blockmask;
        !           220: static sigset_t dgl_signal_oldmask;
        !           221:
        !           222: void
        !           223: signals_block()
        !           224: {
        !           225:     if (!dgl_signal_blocked) {
        !           226:        sigemptyset(&dgl_signal_blockmask);
        !           227:        sigaddset(&dgl_signal_blockmask, SIGHUP);
        !           228:        sigaddset(&dgl_signal_blockmask, SIGINT);
        !           229:        sigaddset(&dgl_signal_blockmask, SIGQUIT);
        !           230:        sigaddset(&dgl_signal_blockmask, SIGTERM);
        !           231:        sigprocmask(SIG_BLOCK, &dgl_signal_blockmask, &dgl_signal_oldmask);
        !           232:        dgl_signal_blocked = 1;
        !           233:     }
        !           234: }
        !           235:
        !           236: void
        !           237: signals_release()
        !           238: {
        !           239:     if (dgl_signal_blocked) {
        !           240:        sigprocmask(SIG_SETMASK, &dgl_signal_oldmask, NULL);
        !           241:        dgl_signal_blocked = 0;
        !           242:     }
        !           243: }
        !           244:
        !           245:
        !           246: /* ************************************************************* */
        !           247:
        !           248: char *
        !           249: get_mainmenu_name()
        !           250: {
        !           251:     if (loggedin) {
        !           252:        if (me && (me->flags & DGLACCT_ADMIN)) return "mainmenu_admin";
        !           253:        return "mainmenu_user";
        !           254:     }
        !           255:     return "mainmenu_anon";
        !           256: }
        !           257:
        !           258:
        !           259: char*
        !           260: gen_ttyrec_filename ()
        !           261: {
        !           262:   time_t rawtime;
        !           263:   struct tm *ptm;
        !           264:   char *ttyrec_filename = calloc(100, sizeof(char));
        !           265:
        !           266:   /* append time to filename */
        !           267:   time (&rawtime);
        !           268:   ptm = gmtime (&rawtime);
        !           269:   snprintf (ttyrec_filename, 100, "%04i-%02i-%02i.%02i:%02i:%02i.ttyrec",
        !           270:             ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
        !           271:             ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
        !           272:   return ttyrec_filename;
        !           273: }
        !           274:
        !           275: /* ************************************************************* */
        !           276:
        !           277: char*
        !           278: gen_inprogress_lock (int game, pid_t pid, char* ttyrec_filename)
        !           279: {
        !           280:   char *lockfile = NULL, filebuf[80];
        !           281:   int fd;
        !           282:   size_t len, wrlen;
        !           283:   struct flock fl = { 0 };
        !           284:
        !           285:   snprintf (filebuf, sizeof(filebuf), "%d\n%d\n%d\n",
        !           286:                  pid, win.ws_row, win.ws_col);
        !           287:
        !           288:   wrlen = strlen(filebuf);
        !           289:
        !           290:   fl.l_type = F_WRLCK;
        !           291:   fl.l_whence = SEEK_SET;
        !           292:   fl.l_start = 0;
        !           293:   fl.l_len = 0;
        !           294:
        !           295:   len = strlen(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL)) + strlen(me->username) + strlen(ttyrec_filename) + 13;
        !           296:   lockfile = calloc(len, sizeof(char));
        !           297:
        !           298:   snprintf (lockfile, len, "%s%s:%s", dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL),
        !           299:             me->username, ttyrec_filename);
        !           300:
        !           301:   fd = open (lockfile, O_WRONLY | O_CREAT, 0644);
        !           302:   if (fcntl (fd, F_SETLKW, &fl) == -1) {
        !           303:       debug_write("cannot fnctl inprogress-lock");
        !           304:     graceful_exit (68);
        !           305:   }
        !           306:
        !           307:   if (write (fd, filebuf, wrlen) != wrlen) {
        !           308:       debug_write("inprogress-lock write");
        !           309:       graceful_exit(70);
        !           310:   }
        !           311:
        !           312:   return lockfile;
        !           313: }
        !           314:
        !           315: /* ************************************************************* */
        !           316:
        !           317: #ifdef USE_SHMEM
        !           318: int hup_shm_idx = -1;
        !           319: char *hup_shm_ttyrec_fn = NULL;
        !           320: #endif
        !           321:
        !           322: void
        !           323: catch_sighup (int signum)
        !           324: {
        !           325:   if (child)
        !           326:     {
        !           327:       sleep (10);
        !           328:       kill (child, SIGHUP);
        !           329:       sleep (5);
        !           330:     }
        !           331: #ifdef USE_SHMEM
        !           332:   signals_block();
        !           333:   if (hup_shm_idx != -1) {
        !           334:       struct dg_shm *shm_dg_data = NULL;
        !           335:       struct dg_shm_game *shm_dg_game = NULL;
        !           336:       shm_init(&shm_dg_data, &shm_dg_game);
        !           337:
        !           338:       shm_sem_wait(shm_dg_data);
        !           339:       if (shm_dg_game[hup_shm_idx].in_use &&
        !           340:          !strcmp(shm_dg_game[hup_shm_idx].ttyrec_fn, hup_shm_ttyrec_fn) &&
        !           341:          (shm_dg_game[hup_shm_idx].nwatchers > 0)) {
        !           342:          shm_dg_game[hup_shm_idx].nwatchers--;
        !           343:       }
        !           344:       shm_sem_post(shm_dg_data);
        !           345:       hup_shm_idx = -1;
        !           346:       free(hup_shm_ttyrec_fn);
        !           347:   }
        !           348:   signals_release();
        !           349: #endif
        !           350:   debug_write("catchup sighup");
        !           351:   graceful_exit (7);
        !           352: }
        !           353:
        !           354: /* ************************************************************* */
        !           355:
        !           356: int
        !           357: dgl_getch(void)
        !           358: {
        !           359:     const int c = getch();
        !           360:     idle_alarm_reset();
        !           361:     return c;
        !           362: }
        !           363:
        !           364: /* ************************************************************* */
        !           365:
        !           366: static void
        !           367: dgl_idle_kill(int signal)
        !           368: {
        !           369:     kill(0, SIGHUP);
        !           370: }
        !           371:
        !           372: void
        !           373: idle_alarm_set_enabled(int enabled)
        !           374: {
        !           375:     signal(SIGALRM, SIG_IGN);
        !           376:     g_idle_alarm_enabled = enabled;
        !           377:     idle_alarm_reset();
        !           378:     if (enabled)
        !           379:         signal(SIGALRM, dgl_idle_kill);
        !           380: }
        !           381:
        !           382: void
        !           383: idle_alarm_reset(void)
        !           384: {
        !           385:     if (g_idle_alarm_enabled && globalconfig.menu_max_idle_time > 0)
        !           386:         alarm(globalconfig.menu_max_idle_time);
        !           387: }
        !           388:
        !           389: /* ************************************************************* */
        !           390:
        !           391:
        !           392: char *
        !           393: bannerstrmangle(char *buf, char *bufnew, int buflen, char *fromstr, char *tostr)
        !           394: {
        !           395:     char *loc;
        !           396:     char *b = buf;
        !           397:
        !           398:     memset (bufnew, 0, buflen);
        !           399:
        !           400:     if (strstr(b, fromstr)) {
        !           401:        int i = 0;
        !           402:        while ((loc = strstr (b, fromstr)) != NULL) {
        !           403:            for (; i < buflen; i++) {
        !           404:                if (loc != b)
        !           405:                    bufnew[i] = *(b++);
        !           406:                else {
        !           407:                    strlcat (bufnew, tostr, buflen);
        !           408:                    b += strlen(fromstr);
        !           409:                    i += strlen(tostr);
        !           410:                    break;
        !           411:                 }
        !           412:
        !           413:                if (strlen (b) == 0)
        !           414:                    break;
        !           415:            }
        !           416:        }
        !           417:
        !           418:        if (*b)
        !           419:            strlcat(bufnew, b, buflen);
        !           420:     } else strncpy(bufnew, buf, buflen);
        !           421:     return bufnew;
        !           422: }
        !           423:
        !           424: void
        !           425: banner_var_add(char *name, char *value, int special)
        !           426: {
        !           427:     struct dg_banner_var *tmp = (struct dg_banner_var *)malloc(sizeof(struct dg_banner_var));
        !           428:
        !           429:     if (!tmp) return;
        !           430:
        !           431:     tmp->name = strdup(name);
        !           432:     tmp->value = strdup(value);
        !           433:     tmp->special = special;
        !           434:     tmp->next = globalconfig.banner_var_list;
        !           435:     globalconfig.banner_var_list = tmp;
        !           436: }
        !           437:
        !           438: void
        !           439: banner_var_free()
        !           440: {
        !           441:     struct dg_banner_var *tmp;
        !           442:     struct dg_banner_var *bv = globalconfig.banner_var_list;
        !           443:     while (bv) {
        !           444:        tmp = bv->next;
        !           445:        free(bv->name);
        !           446:        free(bv->value);
        !           447:        free(bv);
        !           448:        bv = tmp;
        !           449:     }
        !           450:     globalconfig.banner_var_list = NULL;
        !           451: }
        !           452:
        !           453: char *
        !           454: banner_var_resolve(struct dg_banner_var *bv)
        !           455: {
        !           456:   static char tmpbuf[DGL_BANNER_LINELEN+1];
        !           457:   time_t tstamp;
        !           458:   struct tm *ptm;
        !           459:   if (!bv) return NULL;
        !           460:   if (!bv->special) return bv->value;
        !           461:   time(&tstamp);
        !           462:   ptm = gmtime(&tstamp);
        !           463:   strftime(tmpbuf, DGL_BANNER_LINELEN, bv->value, ptm);
        !           464:   return tmpbuf;
        !           465: }
        !           466:
        !           467: char *
        !           468: banner_var_value(char *name)
        !           469: {
        !           470:     struct dg_banner_var *bv = globalconfig.banner_var_list;
        !           471:     while (bv) {
        !           472:        if (!strcmp(bv->name, name)) return banner_var_resolve(bv);
        !           473:        bv = bv->next;
        !           474:     }
        !           475:     return NULL;
        !           476: }
        !           477:
        !           478: void
        !           479: freebanner(struct dg_banner *ban)
        !           480: {
        !           481:     unsigned int l;
        !           482:     if (!ban) return;
        !           483:     l = ban->len;
        !           484:
        !           485:     while (l > 0) {
        !           486:        l--;
        !           487:        free(ban->lines[l]);
        !           488:     }
        !           489:     free(ban->lines);
        !           490:     ban->len = 0;
        !           491:     ban->lines = NULL;
        !           492: }
        !           493:
        !           494: void
        !           495: banner_addline(struct dg_banner *ban, char *line)
        !           496: {
        !           497:     size_t len = strlen(line);
        !           498:     if (!ban) return;
        !           499:     ban->len++;
        !           500:     ban->lines = realloc (ban->lines, sizeof (char *) * ban->len);
        !           501:     if (len >= DGL_BANNER_LINELEN) {
        !           502:        len = DGL_BANNER_LINELEN;
        !           503:        ban->lines[ban->len - 1] = malloc(len);
        !           504:        strncpy(ban->lines[ban->len - 1], line, len);
        !           505:        ban->lines[ban->len - 1][len-1] = '\0';
        !           506:     } else
        !           507:        ban->lines[ban->len - 1] = strdup(line);
        !           508: }
        !           509:
        !           510: void
        !           511: loadbanner (char *fname, struct dg_banner *ban)
        !           512: {
        !           513:   FILE *bannerfile;
        !           514:   char buf[DGL_BANNER_LINELEN+1];
        !           515:   if (ban->len > 23) return;
        !           516:
        !           517:   memset (buf, 0, DGL_BANNER_LINELEN);
        !           518:
        !           519:   bannerfile = fopen (fname, "r");
        !           520:
        !           521:   if (!bannerfile)
        !           522:     {
        !           523:        if (ban->len == 0)
        !           524:            banner_addline(ban, "### dgamelaunch " PACKAGE_VERSION " - network console game launcher");
        !           525:        snprintf(buf, DGL_BANNER_LINELEN, "### NOTE: administrator has not installed a %s file", fname);
        !           526:        banner_addline(ban, buf);
        !           527:        return;
        !           528:     }
        !           529:
        !           530:   while (fgets (buf, DGL_BANNER_LINELEN, bannerfile) != NULL)
        !           531:     {
        !           532:       char bufnew[DGL_BANNER_LINELEN+1];
        !           533:       int slen;
        !           534:
        !           535:       memset (bufnew, 0, DGL_BANNER_LINELEN);
        !           536:
        !           537:       slen = strlen(buf);
        !           538:       if ((slen > 0) && (buf[slen-1] == '\n')) buf[slen-1] = '\0';
        !           539:
        !           540:       strncpy(bufnew, buf, DGL_BANNER_LINELEN);
        !           541:       if (strstr(bufnew, "$INCLUDE(")) {
        !           542:          char *fn = bufnew + 9;
        !           543:          char *fn_end = strchr(fn, ')');
        !           544:          if (fn_end) {
        !           545:              *fn_end = '\0';
        !           546:              if (strcmp(fname, fn)) {
        !           547:                  loadbanner(fn, ban);
        !           548:              }
        !           549:          }
        !           550:       } else {
        !           551:          char tmpbufnew[DGL_BANNER_LINELEN+1];
        !           552:          struct dg_banner_var *bv = globalconfig.banner_var_list;
        !           553:          while (bv) {
        !           554:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, bv->name, banner_var_resolve(bv)), DGL_BANNER_LINELEN);
        !           555:              bv = bv->next;
        !           556:          }
        !           557:          strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$VERSION", PACKAGE_STRING), DGL_BANNER_LINELEN);
        !           558:          if (me && loggedin) {
        !           559:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$USERNAME", me->username), DGL_BANNER_LINELEN);
        !           560:          } else {
        !           561:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$USERNAME", "[Anonymous]"), DGL_BANNER_LINELEN);
        !           562:          }
        !           563:          banner_addline(ban, bufnew);
        !           564:       }
        !           565:
        !           566:       memset (buf, 0, DGL_BANNER_LINELEN);
        !           567:
        !           568:       if (ban->len >= 24)
        !           569:          break;
        !           570:   }
        !           571:
        !           572:   fclose (bannerfile);
        !           573: }
        !           574:
        !           575: void
        !           576: drawbanner (struct dg_banner *ban)
        !           577: {
        !           578:   unsigned int i;
        !           579:   char *tmpch, *tmpch2, *splch;
        !           580:   int attr = 0, oattr = 0;
        !           581:
        !           582:   if (!ban) return;
        !           583:
        !           584:   for (i = 0; i < ban->len; i++) {
        !           585:       char *tmpbuf = strdup(ban->lines[i]);
        !           586:       char *tmpbuf2 = tmpbuf;
        !           587:       int ok = 0;
        !           588:       int x = 1;
        !           589:       do {
        !           590:          ok = 0;
        !           591:          if ((tmpch = strstr(tmpbuf2, "$ATTR("))) {
        !           592:              if ((tmpch2 = strstr(tmpch, ")"))) {
        !           593:                  int spl = 0;
        !           594:                  char *nxttmpch;
        !           595:                  ok = 1;
        !           596:                  oattr = attr;
        !           597:                  attr = A_NORMAL;
        !           598:                  *tmpch = *tmpch2 = '\0';
        !           599:                  tmpch += 6;
        !           600:                  nxttmpch = tmpch;
        !           601:                  do {
        !           602:                      spl = 0;
        !           603:                      splch = strchr(tmpch, ';');
        !           604:                      if (splch && *splch) {
        !           605:                          spl = 1;
        !           606:                          nxttmpch = splch;
        !           607:                          *nxttmpch = '\0';
        !           608:                          nxttmpch++;
        !           609:                      }
        !           610:                      if (tmpch && *tmpch) {
        !           611:                          switch (*tmpch) {
        !           612:                          default: break;
        !           613:                          case '0': case '1': case '2': case '3': case '4':
        !           614:                          case '5': case '6': case '7': case '8': case '9':
        !           615:                              {
        !           616:                                  int num = atoi(tmpch);
        !           617:                                  if (num >= 0 && num <= 15)
        !           618:                                      attr |= color_remap[num];
        !           619:                              }
        !           620:                              break;
        !           621:                          case 'b': attr |= A_BOLD; break;
        !           622:                          case 's': attr |= A_STANDOUT; break;
        !           623:                          case 'u': attr |= A_UNDERLINE; break;
        !           624:                          case 'r': attr |= A_REVERSE; break;
        !           625:                          case 'd': attr |= A_DIM; break;
        !           626:                          }
        !           627:                      } else attr = A_NORMAL;
        !           628:                      tmpch = nxttmpch;
        !           629:                  } while (spl);
        !           630:
        !           631:                  mvaddstr(1 + i, x, tmpbuf2);
        !           632:                  if (oattr) attroff(oattr);
        !           633:                  if (attr) attron(attr);
        !           634:                  x += strlen(tmpbuf2);
        !           635:                  tmpch2++;
        !           636:                  tmpbuf2 = tmpch2;
        !           637:              } else
        !           638:                  mvaddstr (1 + i, x, tmpbuf2);
        !           639:          } else
        !           640:              mvaddstr (1 + i, x, tmpbuf2);
        !           641:       } while (ok);
        !           642:       free(tmpbuf);
        !           643:   }
        !           644: }
        !           645:
        !           646: void
        !           647: shm_sem_wait(struct dg_shm *shm_dg_data)
        !           648: {
        !           649: #ifdef USE_SHMEM
        !           650:     if (sem_wait(&(shm_dg_data->dg_sem)) == -1) {
        !           651:        debug_write("sem_wait");
        !           652:        graceful_exit(77);
        !           653:     }
        !           654: #endif
        !           655: }
        !           656:
        !           657: void
        !           658: shm_sem_post(struct dg_shm *shm_dg_data)
        !           659: {
        !           660: #ifdef USE_SHMEM
        !           661:     if (sem_post(&(shm_dg_data->dg_sem)) == -1) {
        !           662:        debug_write("sem_post");
        !           663:        graceful_exit(78);
        !           664:     }
        !           665: #endif
        !           666: }
        !           667:
        !           668: void
        !           669: shm_update(struct dg_shm *shm_dg_data, struct dg_game **games, int len)
        !           670: {
        !           671: #ifdef USE_SHMEM
        !           672:     int di, i;
        !           673:     struct dg_shm_game *shm_dg_game = (struct dg_shm_game *)(shm_dg_data + sizeof(struct dg_shm));
        !           674:
        !           675:     signals_block();
        !           676:     shm_sem_wait(shm_dg_data);
        !           677:
        !           678:     for (di = 0; di < shm_dg_data->max_n_games; di++)
        !           679:        if (shm_dg_game[di].in_use) {
        !           680:            int delgame = 1;
        !           681:            for (i = 0; i < len; i++) {
        !           682:                if (!strcmp(games[i]->ttyrec_fn, shm_dg_game[di].ttyrec_fn)) {
        !           683:                    delgame = 0;
        !           684:                    games[i]->is_in_shm = 1;
        !           685:                    games[i]->shm_idx = di;
        !           686:                    games[i]->nwatchers = shm_dg_game[di].nwatchers;
        !           687:                    break;
        !           688:                }
        !           689:            }
        !           690:            if (delgame) {
        !           691:                shm_dg_game[di].in_use = 0;
        !           692:                if (shm_dg_data->cur_n_games > 0) shm_dg_data->cur_n_games--;
        !           693:            }
        !           694:        }
        !           695:
        !           696:     if (shm_dg_data->cur_n_games < shm_dg_data->max_n_games) {
        !           697:        for (i = 0; i < len; i++)
        !           698:            if (!games[i]->is_in_shm) {
        !           699:                for (di = 0; di < shm_dg_data->max_n_games; di++)
        !           700:                    if (!shm_dg_game[di].in_use) {
        !           701:                        shm_dg_game[di].in_use = 1;
        !           702:                        shm_dg_game[di].nwatchers = 0;
        !           703:                        games[i]->nwatchers = 0;
        !           704:                        games[i]->is_in_shm = 1;
        !           705:                        games[i]->shm_idx = di;
        !           706:                        shm_dg_data->cur_n_games++;
        !           707:                        strncpy(shm_dg_game[di].ttyrec_fn, games[i]->ttyrec_fn, 150);
        !           708:                        break;
        !           709:                    }
        !           710:            }
        !           711:     }
        !           712:
        !           713:     shm_sem_post(shm_dg_data);
        !           714:     signals_release();
        !           715: #endif
        !           716: }
        !           717:
        !           718: void
        !           719: shm_mk_keys(key_t *shm_key, key_t *shm_sem_key)
        !           720: {
        !           721: #ifdef USE_SHMEM
        !           722:     if ((*shm_key = ftok(globalconfig.passwd, 'R')) == -1) {
        !           723:        debug_write("ftok shm_key");
        !           724:        graceful_exit(71);
        !           725:     }
        !           726:     if ((*shm_sem_key = ftok(globalconfig.passwd, 'S')) == -1) {
        !           727:        debug_write("ftok shm_sem_key");
        !           728:        graceful_exit(72);
        !           729:     }
        !           730: #endif
        !           731: }
        !           732:
        !           733: #ifdef USE_SHMEM
        !           734: int
        !           735: shm_free()
        !           736: {
        !           737:     key_t shm, sem;
        !           738:     int   shm_id;
        !           739:     int shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
        !           740:     shm_mk_keys(&shm, &sem);
        !           741:     if ((shm_id = shmget(shm, shm_size, 0644)) != -1) {
        !           742:        shmctl(shm_id, IPC_RMID, NULL);
        !           743:        return 0;
        !           744:     }
        !           745:     return 1;
        !           746: }
        !           747: #endif
        !           748:
        !           749: void
        !           750: shm_init(struct dg_shm **shm_dg_data, struct dg_shm_game **shm_dg_game)
        !           751: {
        !           752: #ifdef USE_SHMEM
        !           753:   key_t shm_key;
        !           754:   key_t shm_sem_key;
        !           755:   int   shm_id;
        !           756:   int   shm_size;
        !           757:   void *shm_data = NULL;
        !           758:   int   shm_data_existed = 0;
        !           759:
        !           760:   shm_mk_keys(&shm_key, &shm_sem_key);
        !           761:
        !           762:   /* max. shm_n_games simultaneous games recorded in the shared memory */
        !           763:   shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
        !           764:
        !           765:   /* connect to (and possibly create) the segment */
        !           766:   if ((shm_id = shmget(shm_key, shm_size, 0644 | IPC_CREAT | IPC_EXCL)) == -1) {
        !           767:       /* creation failed, so it already exists. attach to it */
        !           768:       shm_data_existed = 1;
        !           769:       if ((shm_id = shmget(shm_key, shm_size, 0644)) == -1) {
        !           770:          debug_write("shmget");
        !           771:          graceful_exit(73);
        !           772:       }
        !           773:   }
        !           774:
        !           775:   /* attach to the segment to get a pointer to it: */
        !           776:   shm_data = shmat(shm_id, (void *)0, 0);
        !           777:   if (shm_data == (char *)(-1)) {
        !           778:       debug_write("shmat");
        !           779:       graceful_exit(74);
        !           780:   }
        !           781:   if (!shm_data) {
        !           782:       debug_write("shm_data == null");
        !           783:       graceful_exit(75);
        !           784:   }
        !           785:
        !           786:   (*shm_dg_data) = (struct dg_shm *)shm_data;
        !           787:   (*shm_dg_game) = (struct dg_shm_game *)((*shm_dg_data) + sizeof(struct dg_shm));
        !           788:
        !           789:   if (!shm_data_existed && shm_data) {
        !           790:       memset(*shm_dg_game, 0, shm_n_games*sizeof(struct dg_shm_game));
        !           791:       (*shm_dg_data)->max_n_games = shm_n_games;
        !           792:       (*shm_dg_data)->cur_n_games = 0;
        !           793:       if (sem_init(&((*shm_dg_data)->dg_sem), 1,1) == -1) {
        !           794:          debug_write("sem_init");
        !           795:          graceful_exit(76);
        !           796:       }
        !           797:   }
        !           798: #endif /* USE_SHMEM */
        !           799: }
        !           800:
        !           801: #ifdef USE_SHMEM
        !           802: void
        !           803: shm_dump()
        !           804: {
        !           805:     struct dg_shm *shm_dg_data = NULL;
        !           806:     struct dg_shm_game *shm_dg_game = NULL;
        !           807:     int di, unused = -1;
        !           808:     shm_init(&shm_dg_data, &shm_dg_game);
        !           809:     shm_sem_wait(shm_dg_data);
        !           810:     for (di = 0; di < shm_dg_data->max_n_games; di++) {
        !           811:        if (shm_dg_game[di].in_use) {
        !           812:            if (unused != -1) {
        !           813:                if (unused != di-1)
        !           814:                    fprintf(stderr, "%i-%i:\tunused\n", unused, di-1);
        !           815:                else
        !           816:                    fprintf(stderr, "%i:\tunused\n", unused);
        !           817:                unused = -1;
        !           818:            }
        !           819:            fprintf(stderr, "%i:\t\"%s\"\twatchers:%li\n", di, shm_dg_game[di].ttyrec_fn, shm_dg_game[di].nwatchers);
        !           820:        } else {
        !           821:            if (unused == -1) unused = di;
        !           822:        }
        !           823:     }
        !           824:     if (unused != -1) {
        !           825:        if (unused != di-1)
        !           826:            fprintf(stderr, "%i-%i:\tunused\n", unused, di-1);
        !           827:        else
        !           828:            fprintf(stderr, "%i:\tunused\n", unused);
        !           829:        unused = -1;
        !           830:     }
        !           831:     shm_sem_post(shm_dg_data);
        !           832:     shmdt(shm_dg_data);
        !           833: }
        !           834: #endif
        !           835:
        !           836: static
        !           837: struct dg_watchcols **
        !           838: globalconfig_watch_columns()
        !           839: {
        !           840:     if (globalconfig.n_watch_columns)
        !           841:         return globalconfig.watch_columns;
        !           842:
        !           843:     if (!*default_watchcols_list) {
        !           844:         int i;
        !           845:         for (i = 0; i < ARRAY_SIZE(default_watchcols); ++i)
        !           846:             default_watchcols_list[i] = &default_watchcols[i];
        !           847:     }
        !           848:     return default_watchcols_list;
        !           849: }
        !           850:
        !           851: static
        !           852: int
        !           853: watchcol_find_index(struct dg_watchcols **watchcols,
        !           854:                     int sortmode)
        !           855: {
        !           856:     int i;
        !           857:     for (i = 0; watchcols[i]; ++i)
        !           858:         if (watchcols[i]->sortmode == sortmode)
        !           859:             return i;
        !           860:     return -1;
        !           861: }
        !           862:
        !           863: static
        !           864: void
        !           865: sortmode_increment(struct dg_watchcols **watchcols,
        !           866:                    dg_sortmode *sortmode,
        !           867:                    int direction)
        !           868: {
        !           869:     int watch_column_index = watchcol_find_index(watchcols, *sortmode);
        !           870:     int n_watchcols;
        !           871:     int wrap_count = 0;
        !           872:     const dg_sortmode old_sortmode = *sortmode;
        !           873:
        !           874:     for (n_watchcols = 0; watchcols[n_watchcols]; ++n_watchcols)
        !           875:         ;
        !           876:
        !           877:     if (watch_column_index == -1 || !n_watchcols)
        !           878:         return;
        !           879:
        !           880:     do {
        !           881:         watch_column_index += direction;
        !           882:
        !           883:         if (watch_column_index < 0) {
        !           884:             watch_column_index = n_watchcols - 1;
        !           885:             ++wrap_count;
        !           886:         } else if (watch_column_index >= n_watchcols) {
        !           887:             watch_column_index = 0;
        !           888:             ++wrap_count;
        !           889:         }
        !           890:
        !           891:         *sortmode = watchcols[watch_column_index]->sortmode;
        !           892:     } while (wrap_count < 2 && !*sortmode);
        !           893:
        !           894:     if (!*sortmode)
        !           895:         *sortmode = old_sortmode;
        !           896: }
        !           897:
        !           898: char *
        !           899: get_timediff(time_t ctime, long seconds)
        !           900: {
        !           901:     static char data[32];
        !           902:     long secs, mins, hours;
        !           903:
        !           904:     secs = (ctime - seconds);
        !           905:
        !           906:     if (showplayers) {
        !           907:        snprintf(data, 10, "%ld", secs);
        !           908:        return data;
        !           909:     }
        !           910:
        !           911:     hours = (secs / 3600);
        !           912:     secs -= (hours * 3600);
        !           913:     mins = (secs / 60) % 60;
        !           914:     secs -= (mins*60);
        !           915:     if (hours)
        !           916:        snprintf(data, 10, "%ldh %ldm", hours, mins);
        !           917:     else if (mins)
        !           918:        snprintf(data, 10, "%ldm %lds", mins, secs);
        !           919:     else if (secs > 4)
        !           920:        snprintf(data, 30, "%lds", secs);
        !           921:     else
        !           922:        snprintf(data, 10, " ");
        !           923:     return data;
        !           924: }
        !           925:
        !           926: static
        !           927: void
        !           928: game_get_column_data(struct dg_game *game,
        !           929:                      char selectorchar,
        !           930:                      time_t ctime, struct dg_shm_game *shm_dg_game,
        !           931:                      char *data, int bufsz, int *hilite,
        !           932:                      dg_sortmode which_data)
        !           933: {
        !           934:     *data = 0;
        !           935:
        !           936:     switch (which_data) {
        !           937:     default: break;
        !           938:     case SORTMODE_NONE:
        !           939:         data[0] = selectorchar; data[1] = '\0';
        !           940:         break;
        !           941:
        !           942:     case SORTMODE_USERNAME:
        !           943:         snprintf(data, bufsz, "%s", game->name);
        !           944:         break;
        !           945:
        !           946:     case SORTMODE_GAMENUM:
        !           947:         snprintf(data, bufsz, "%s",
        !           948:                  myconfig[game->gamenum]->shortname);
        !           949:         break;
        !           950:
        !           951:     case SORTMODE_WINDOWSIZE:
        !           952:         snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row);
        !           953:        if (showplayers)
        !           954:                snprintf(data, bufsz, "%dx%d", game->ws_col, game->ws_row);
        !           955:        else
        !           956:                snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row);
        !           957:         if ((game->ws_col > COLS || game->ws_row > LINES))
        !           958:             *hilite = CLR_RED;
        !           959:         break;
        !           960:
        !           961:     case SORTMODE_STARTTIME:
        !           962:         snprintf(data, bufsz, "%s %s", game->date,
        !           963:                  game->time);
        !           964:         break;
        !           965:
        !           966:     case SORTMODE_DURATION:
        !           967:        {
        !           968:            /* TODO: populate_games() should put st_ctime into game struct */
        !           969:            struct tm timetm;
        !           970:            char tmptimebuf[32];
        !           971:            snprintf(tmptimebuf, 30, "%s %s", game->date, game->time);
        !           972:            tmptimebuf[31] = '\0';
        !           973:            strptime(tmptimebuf, "%Y-%m-%d %H:%M:%S", &timetm);
        !           974:            snprintf(data, 10, "%s", get_timediff(ctime, mktime(&timetm)));
        !           975:        }
        !           976:        break;
        !           977:
        !           978:     case SORTMODE_IDLETIME:
        !           979:        snprintf(data, 10, "%s", get_timediff(ctime, game->idle_time));
        !           980:         break;
        !           981:
        !           982:     case SORTMODE_EXTRA_INFO:
        !           983:         if (game->extra_info)
        !           984:             strlcpy(data, game->extra_info, bufsz);
        !           985:         break;
        !           986:
        !           987: #ifdef USE_SHMEM
        !           988:     case SORTMODE_WATCHERS:
        !           989:         snprintf(data, bufsz, "%li",
        !           990:                  (game->is_in_shm ?
        !           991:                   shm_dg_game[game->shm_idx].nwatchers : -1));
        !           992:         break;
        !           993: #endif
        !           994:     }
        !           995:     data[bufsz - 1] = '\0';
        !           996: }
        !           997:
        !           998: void
        !           999: inprogressmenu (int gameid)
        !          1000: {
        !          1001:     const char *selectorchars = "abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
        !          1002:   int i, menuchoice, len = 20, offset = 0;
        !          1003:   static dg_sortmode sortmode = NUM_SORTMODES;
        !          1004:   struct dg_game **games = NULL;
        !          1005:   char ttyrecname[130], gametype[10], idletime[10];
        !          1006:   sigset_t oldmask, toblock;
        !          1007:   int idx = -1;
        !          1008:   int shm_idx = -1;
        !          1009:   int max_height = -1;
        !          1010:   int selected = -1;
        !          1011:
        !          1012:   int resizex = -1;
        !          1013:   int resizey = -1;
        !          1014:
        !          1015:   char *selectedgame = NULL;
        !          1016:
        !          1017:   int abs_max_height;
        !          1018:   int top_banner_hei = 5;
        !          1019:   int btm_banner_hei = 3;
        !          1020:   int btm;
        !          1021:
        !          1022:   int title_attr = A_STANDOUT;
        !          1023:   int selected_attr = A_BOLD;
        !          1024:
        !          1025:   int require_enter = 0; /* TODO: make configurable */
        !          1026:
        !          1027:   time_t ctime;
        !          1028:
        !          1029:   struct dg_shm *shm_dg_data = NULL;
        !          1030:   struct dg_shm_game *shm_dg_game = NULL;
        !          1031:
        !          1032:   struct dg_watchcols **watchcols = globalconfig_watch_columns();
        !          1033:   struct dg_watchcols **curr_watchcol;
        !          1034:
        !          1035:   if (sortmode == NUM_SORTMODES)
        !          1036:       sortmode = globalconfig.sortmode;
        !          1037:
        !          1038:   abs_max_height = strlen(selectorchars);
        !          1039:
        !          1040:   shm_init(&shm_dg_data, &shm_dg_game);
        !          1041:
        !          1042:   games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
        !          1043:   shm_update(shm_dg_data, games, len);
        !          1044:   games = sort_games (games, len, sortmode);
        !          1045:
        !          1046:   while (1)
        !          1047:     {
        !          1048:        term_resize_check();
        !          1049:        max_height = dgl_local_LINES - (top_banner_hei + btm_banner_hei) - 1;
        !          1050:        if (max_height < 2) {
        !          1051:            free_populated_games(games, len);
        !          1052:            return;
        !          1053:        }
        !          1054:        if (max_height > abs_max_height) max_height = abs_max_height;
        !          1055:
        !          1056:       if (len == 0)
        !          1057:         offset = 0;
        !          1058:
        !          1059:       erase ();
        !          1060:       drawbanner (&banner);
        !          1061:
        !          1062:       if (len > 0) {
        !          1063:          while (offset >= len) { offset -= max_height; }
        !          1064:          if (offset < 0) offset = 0;
        !          1065:          mvaddstr (3, 1, "The following games are in progress:");
        !          1066:
        !          1067:          for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
        !          1068:               struct dg_watchcols *wcol = *curr_watchcol;
        !          1069:              char *col = wcol->colname;
        !          1070:              int x = wcol->x;
        !          1071:              while (*col == ' ') { x++; col++; }
        !          1072:              if (sortmode == wcol->sortmode) attron(title_attr);
        !          1073:              mvprintw(top_banner_hei, x, col);
        !          1074:              if (sortmode == wcol->sortmode) attroff(title_attr);
        !          1075:          }
        !          1076:       }
        !          1077:
        !          1078:       signals_block();
        !          1079:       shm_sem_wait(shm_dg_data);
        !          1080:
        !          1081:       (void) time(&ctime);
        !          1082:
        !          1083:       for (i = 0; i < max_height; i++)
        !          1084:         {
        !          1085:           if (i + offset >= len)
        !          1086:             break;
        !          1087:
        !          1088:          if (i + offset == selected) attron(selected_attr);
        !          1089:
        !          1090:          for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
        !          1091:               struct dg_watchcols *col = *curr_watchcol;
        !          1092:              char tmpbuf[80];
        !          1093:              int hilite = 0;
        !          1094:               game_get_column_data(games[i + offset],
        !          1095:                                    selectorchars[i],
        !          1096:                                    ctime, shm_dg_game,
        !          1097:                                    tmpbuf, sizeof tmpbuf, &hilite,
        !          1098:                                    (dg_sortmode)col->dat);
        !          1099:              if (hilite) attron(hilite);
        !          1100:              mvprintw(top_banner_hei + 1 + i, col->x, col->fmt, tmpbuf);
        !          1101:              if (hilite) {
        !          1102:                  attron(CLR_NORMAL);
        !          1103:                  hilite = 0;
        !          1104:              }
        !          1105:          }
        !          1106:
        !          1107:          if (i + offset == selected) attroff(selected_attr);
        !          1108:
        !          1109:         }
        !          1110:
        !          1111:       shm_sem_post(shm_dg_data);
        !          1112:       signals_release();
        !          1113:
        !          1114:       btm = dgl_local_LINES-btm_banner_hei-top_banner_hei;
        !          1115:       if (len <= max_height)
        !          1116:          btm = i+1;
        !          1117:
        !          1118:       if (len > 0) {
        !          1119:          mvprintw ((btm+top_banner_hei), 1, "(%d-%d of %d)", offset + 1, offset + i, len);
        !          1120:          mvaddstr ((btm+2+top_banner_hei), 1, "Watch which game? ('?' for help) => ");
        !          1121:       } else {
        !          1122:          mvprintw(top_banner_hei,4,"Sorry, no games available for viewing.");
        !          1123:          mvaddstr((btm+2+top_banner_hei), 1, "Press 'q' to return, or '?' for help => ");
        !          1124:       }
        !          1125:
        !          1126:       refresh ();
        !          1127:
        !          1128:       switch ((menuchoice = dgl_getch ()))
        !          1129:         {
        !          1130:        case KEY_DOWN:
        !          1131:            selected++;
        !          1132:            if (selected >= len) selected = 0;
        !          1133:            while (selected < offset) offset -= max_height;
        !          1134:            while (selected >= offset+max_height) offset += max_height;
        !          1135:            break;
        !          1136:        case KEY_UP:
        !          1137:            if (selected != -1) {
        !          1138:                if (selected == 0) selected = len;
        !          1139:                selected--;
        !          1140:            } else selected = len-1;
        !          1141:            while (selected < offset) offset -= max_height;
        !          1142:            while (selected >= offset+max_height) offset += max_height;
        !          1143:            break;
        !          1144:        case '*':
        !          1145:            if (len > 0) {
        !          1146:                int cnt = 20;
        !          1147:                (void) time(&ctime);
        !          1148:                do {
        !          1149:                    idx = random() % len;
        !          1150:                } while ((--cnt > 0) ||
        !          1151:                         !((games[idx]->ws_col <= COLS) &&
        !          1152:                          (games[idx]->ws_row <= LINES) &&
        !          1153:                          ((ctime - games[idx]->idle_time) < 15)));
        !          1154:                selected = idx;
        !          1155:                goto watchgame;
        !          1156:            }
        !          1157:            break;
        !          1158:        case '?':
        !          1159:            (void) runmenuloop(dgl_find_menu("watchmenu_help"));
        !          1160:            break;
        !          1161:        case '/':
        !          1162:            {
        !          1163:                int match = -1;
        !          1164:               int firstmatch = -1;
        !          1165:               int nmatches = 0;
        !          1166:                char findname[DGL_PLAYERNAMELEN+1];
        !          1167:               if (len <= 0) break;
        !          1168:                findname[0] = '\0';
        !          1169:               mvprintw ((btm+2+top_banner_hei), 1, "Watch which player? =>                 "); /* stupid... */
        !          1170:               mvaddstr ((btm+2+top_banner_hei), 1, "Watch which player? => ");
        !          1171:                if ((mygetnstr(findname, DGL_PLAYERNAMELEN, 1) == OK) && (strlen(findname) > 1)) {
        !          1172:                   int mlen = strlen(findname);
        !          1173:                    for (i = 0; i < len; i++)
        !          1174:                        if (!strncasecmp(games[i]->name, findname, mlen)) {
        !          1175:                           if (firstmatch == -1) firstmatch = i;
        !          1176:                            match = i;
        !          1177:                           nmatches++;
        !          1178:                        }
        !          1179:                   if (nmatches > 1)
        !          1180:                       match = firstmatch;
        !          1181:                    if (match > -1) {
        !          1182:                       idx = match;
        !          1183:                       selected = idx;
        !          1184:                       goto watchgame;
        !          1185:                    }
        !          1186:                }
        !          1187:            }
        !          1188:            break;
        !          1189:        case KEY_NPAGE:
        !          1190:         case '>':
        !          1191:            if ((offset + max_height) < len) offset += max_height;
        !          1192:           break;
        !          1193:        case KEY_PPAGE:
        !          1194:         case '<':
        !          1195:           if ((offset - max_height) < 0)
        !          1196:              offset = 0;
        !          1197:           else
        !          1198:             offset -= max_height;
        !          1199:           break;
        !          1200:
        !          1201:        case ERR:
        !          1202:        case 'q': case 'Q':
        !          1203:         case '\x1b':
        !          1204:            goto leavewatchgame;
        !          1205:        case KEY_RIGHT:
        !          1206:        case '.':
        !          1207:             sortmode_increment(watchcols, &sortmode, 1);
        !          1208:            break;
        !          1209:        case KEY_LEFT:
        !          1210:        case ',':
        !          1211:             sortmode_increment(watchcols, &sortmode, -1);
        !          1212:            break;
        !          1213:
        !          1214:        case 12: case 18: /* ^L, ^R */
        !          1215:           if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
        !          1216:          clear ();
        !          1217:          break;
        !          1218:
        !          1219:        case 13:
        !          1220:        case 10:
        !          1221:        case KEY_ENTER:
        !          1222:            if (selected >= 0 && selected < len) {
        !          1223:                idx = selected;
        !          1224:                goto watchgame;
        !          1225:            }
        !          1226:            break;
        !          1227:
        !          1228:         default:
        !          1229:            if (strchr(selectorchars, menuchoice) && (len > 0)) {
        !          1230:                int sidx = strchr(selectorchars, menuchoice) - selectorchars;
        !          1231:
        !          1232:                if ((sidx > max_height) || (sidx >= len)) {
        !          1233:                    selected = -1;
        !          1234:                    break;
        !          1235:                }
        !          1236:
        !          1237:              idx = sidx + offset;
        !          1238:              if (require_enter) {
        !          1239:                  if (selected == idx) selected = -1;
        !          1240:                  else selected = idx;
        !          1241:                  break;
        !          1242:              } else selected = idx;
        !          1243: watchgame:
        !          1244:              if (!(idx >= 0 && idx < len && games[idx] && games[idx]->name))
        !          1245:                  goto leavewatchgame;
        !          1246:               /* valid choice has been made */
        !          1247:               chosen_name = strdup (games[idx]->name);
        !          1248:               snprintf (ttyrecname, 130, "%s",
        !          1249:                         games[idx]->ttyrec_fn);
        !          1250:
        !          1251:               clear ();
        !          1252:               refresh ();
        !          1253:               endwin ();
        !          1254:              if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
        !          1255: #ifdef USE_SHMEM
        !          1256:              signals_block();
        !          1257:              if (games[idx]->is_in_shm) {
        !          1258:                  shm_idx = games[idx]->shm_idx;
        !          1259:                  shm_sem_wait(shm_dg_data);
        !          1260:                  if (shm_dg_game[shm_idx].in_use &&
        !          1261:                      !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn)) {
        !          1262:                      shm_dg_game[shm_idx].nwatchers++;
        !          1263:                      games[idx]->nwatchers++;
        !          1264:                  }
        !          1265:                  hup_shm_idx = shm_idx;
        !          1266:                  hup_shm_ttyrec_fn = strdup(games[idx]->ttyrec_fn);
        !          1267:                  shm_sem_post(shm_dg_data);
        !          1268:              }
        !          1269:              signals_release();
        !          1270: #endif
        !          1271:              resizey = games[idx]->ws_row;
        !          1272:              resizex = games[idx]->ws_col;
        !          1273:              if (loggedin)
        !          1274:                  setproctitle("%s [watching %s]", me->username, chosen_name);
        !          1275:              else
        !          1276:                  setproctitle("<Anonymous> [watching %s]", chosen_name);
        !          1277:               ttyplay_main (ttyrecname, 1, resizex, resizey);
        !          1278:              if (loggedin)
        !          1279:                  setproctitle("%s", me->username);
        !          1280:              else
        !          1281:                  setproctitle("<Anonymous>");
        !          1282: #ifdef USE_SHMEM
        !          1283:              signals_block();
        !          1284:              if (games[idx]->is_in_shm) {
        !          1285:                  hup_shm_idx = -1;
        !          1286:                  free(hup_shm_ttyrec_fn);
        !          1287:                  shm_sem_wait(shm_dg_data);
        !          1288:                  if (shm_dg_game[shm_idx].in_use &&
        !          1289:                      !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn) &&
        !          1290:                      (shm_dg_game[shm_idx].nwatchers > 0)) {
        !          1291:                      shm_dg_game[shm_idx].nwatchers--;
        !          1292:                      games[idx]->nwatchers--;
        !          1293:                  }
        !          1294:                  shm_sem_post(shm_dg_data);
        !          1295:              }
        !          1296:              signals_release();
        !          1297: #endif
        !          1298:               initcurses ();
        !          1299:              redrawwin(stdscr);
        !          1300:             }
        !          1301:         }
        !          1302:
        !          1303:       if (selected >= 0 && selected < len)
        !          1304:          selectedgame = strdup(games[selected]->name);
        !          1305:       games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
        !          1306:       shm_update(shm_dg_data, games, len);
        !          1307:       games = sort_games (games, len, sortmode);
        !          1308:       if (selectedgame) {
        !          1309:          selected = -1;
        !          1310:          for (i = 0; i < len; i++)
        !          1311:              if (!strcmp(games[i]->name, selectedgame)) {
        !          1312:                  selected = i;
        !          1313:                  break;
        !          1314:              }
        !          1315:          free(selectedgame);
        !          1316:          selectedgame = NULL;
        !          1317:       }
        !          1318:     }
        !          1319: leavewatchgame:
        !          1320:   free_populated_games(games, len);
        !          1321: #ifdef USE_SHMEM
        !          1322:   shmdt(shm_dg_data);
        !          1323: #endif
        !          1324: }
        !          1325:
        !          1326: void
        !          1327: inprogressdisplay (int gameid)
        !          1328: {
        !          1329:     const char *selectorchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
        !          1330:   int i, len = 20;
        !          1331:   static dg_sortmode sortmode = NUM_SORTMODES;
        !          1332:   struct dg_game **games = NULL;
        !          1333:   int shm_idx = -1;
        !          1334:
        !          1335:   time_t ctime;
        !          1336:
        !          1337:   struct dg_shm *shm_dg_data = NULL;
        !          1338:   struct dg_shm_game *shm_dg_game = NULL;
        !          1339:
        !          1340:   struct dg_watchcols **watchcols = globalconfig_watch_columns();
        !          1341:   struct dg_watchcols **curr_watchcol;
        !          1342:
        !          1343:   if (sortmode == NUM_SORTMODES)
        !          1344:       sortmode = globalconfig.sortmode;
        !          1345:
        !          1346:   shm_init(&shm_dg_data, &shm_dg_game);
        !          1347:
        !          1348:   games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
        !          1349:   shm_update(shm_dg_data, games, len);
        !          1350:   games = sort_games (games, len, sortmode);
        !          1351:
        !          1352:   signals_block();
        !          1353:   shm_sem_wait(shm_dg_data);
        !          1354:
        !          1355:   (void) time(&ctime);
        !          1356:
        !          1357:   for (i = 0; i < 100; i++) {
        !          1358:     if (i >= len)
        !          1359:       break;
        !          1360:
        !          1361:     for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
        !          1362:       struct dg_watchcols *col = *curr_watchcol;
        !          1363:       if ((dg_sortmode)col->dat == SORTMODE_NONE)
        !          1364:         continue;
        !          1365:       char tmpbuf[80];
        !          1366:       int hilite = 0;
        !          1367:       game_get_column_data(games[i],
        !          1368:         selectorchars[i],
        !          1369:         ctime, shm_dg_game,
        !          1370:         tmpbuf, sizeof tmpbuf, &hilite, (dg_sortmode)col->dat);
        !          1371:       fprintf(stdout, "%s#", tmpbuf); /* format in col->fmt */
        !          1372:     }
        !          1373:     fprintf(stdout, "\n");
        !          1374:   }
        !          1375:
        !          1376:   shm_sem_post(shm_dg_data);
        !          1377:   signals_release();
        !          1378:
        !          1379:   free_populated_games(games, len);
        !          1380:
        !          1381: #ifdef USE_SHMEM
        !          1382:   shmdt(shm_dg_data);
        !          1383: #endif
        !          1384: }
        !          1385:
        !          1386: /* ************************************************************* */
        !          1387:
        !          1388: /*
        !          1389:  * Check email address, returns 1 if valid, 0 otherwise.
        !          1390:  * Doesn't recognize addresses with parts in double-quotes.
        !          1391:  * Addresses with a colon in them are always rejected.
        !          1392:  */
        !          1393: int
        !          1394: check_email (char *s)
        !          1395: {
        !          1396:   char *atomchars = "!#$%&'*+-/=?^_`{|}~" "0123456789"
        !          1397:     "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        !          1398:   int f;
        !          1399:
        !          1400:   if (*s == '@')
        !          1401:     return 0;
        !          1402:
        !          1403:   while (*s != '\0' && *s != '@')
        !          1404:     {
        !          1405:       if (strchr(atomchars, *s) == NULL)
        !          1406:         return 0;
        !          1407:       s++;
        !          1408:       if (*s == '.')
        !          1409:         s++;
        !          1410:     }
        !          1411:
        !          1412:   if (*s == '\0')
        !          1413:     return 0;
        !          1414:   s++;
        !          1415:
        !          1416:   f = 0;
        !          1417:   while (*s != '\0')
        !          1418:     {
        !          1419:       if (strchr(atomchars, *s) == NULL)
        !          1420:         return 0;
        !          1421:       s++;
        !          1422:       if (*s == '.')
        !          1423:         s++, f = 1;
        !          1424:     }
        !          1425:
        !          1426:   return f;
        !          1427: }
        !          1428:
        !          1429: void
        !          1430: change_email ()
        !          1431: {
        !          1432:   char buf[81];
        !          1433:
        !          1434:   clear();
        !          1435:
        !          1436:   if (me->flags & DGLACCT_EMAIL_LOCK) {
        !          1437:       drawbanner(&banner);
        !          1438:       mvprintw(5, 1, "Sorry, you cannot change the email.--More--");
        !          1439:       dgl_getch();
        !          1440:       return;
        !          1441:   }
        !          1442:
        !          1443:   for (;;)
        !          1444:   {
        !          1445:       drawbanner(&banner);
        !          1446:
        !          1447:     mvprintw(3, 1, "Your current email is: %s", me->email);
        !          1448:     mvaddstr(4, 1, "Please enter a new one (max 80 chars; blank line aborts)");
        !          1449:     mvaddstr(6, 1, "=> ");
        !          1450:
        !          1451:     if (mygetnstr (buf, 80, 1) != OK)
        !          1452:        return;
        !          1453:
        !          1454:     if (*buf == '\0')
        !          1455:       return;
        !          1456:     else if (!strcmp(me->email, buf))
        !          1457:     {
        !          1458:       clear();
        !          1459:       mvaddstr (8, 1, "That's the same one as before. Try again?");
        !          1460:       move(1,1);
        !          1461:     }
        !          1462:     else if (check_email (buf))
        !          1463:     {
        !          1464:       mvprintw (8, 1, "Changing email address to '%s'. Confirm (y/n): ", buf);
        !          1465:       if (dgl_getch() == 'y')
        !          1466:       {
        !          1467:        free(me->email);
        !          1468:        me->email = strdup(buf);
        !          1469:        writefile(0);
        !          1470:        return;
        !          1471:       }
        !          1472:       else
        !          1473:       {
        !          1474:        mvaddstr(9, 1, "No changes made. Press any key to continue...");
        !          1475:        dgl_getch();
        !          1476:        return;
        !          1477:       }
        !          1478:     }
        !          1479:     else
        !          1480:     {
        !          1481:       clear();
        !          1482:       mvaddstr (8, 1, "That doesn't look like an email address to me.");
        !          1483:       move(1,1);
        !          1484:     }
        !          1485:   }
        !          1486: }
        !          1487:
        !          1488: int
        !          1489: changepw (int dowrite)
        !          1490: {
        !          1491:   char buf[DGL_PASSWDLEN+1];
        !          1492:   int error = 2;
        !          1493:
        !          1494:   /* A precondition is that struct `me' exists because we can be not-yet-logged-in. */
        !          1495:   if (!me) {
        !          1496:       debug_write("no 'me' in changepw");
        !          1497:     graceful_exit (122);        /* Die. */
        !          1498:   }
        !          1499:
        !          1500:   if (me->flags & DGLACCT_PASSWD_LOCK) {
        !          1501:       clear();
        !          1502:       drawbanner(&banner);
        !          1503:       mvprintw(5, 1, "Sorry, you cannot change the password.--More--");
        !          1504:       dgl_getch();
        !          1505:       return 0;
        !          1506:   }
        !          1507:
        !          1508:   while (error)
        !          1509:     {
        !          1510:       char repeatbuf[DGL_PASSWDLEN+1];
        !          1511:       clear ();
        !          1512:
        !          1513:       drawbanner (&banner);
        !          1514:
        !          1515:       mvprintw (5, 1,
        !          1516:                 "Please enter a%s password. Remember that this is sent over the net",
        !          1517:                 loggedin ? " new" : "");
        !          1518:       mvaddstr (6, 1,
        !          1519:                 "in plaintext, so make it something new and expect it to be relatively");
        !          1520:       mvaddstr (7, 1, "insecure.");
        !          1521:       mvprintw (8, 1,
        !          1522:                 "%i character max. No ':' characters. Blank line to abort.", DGL_PASSWDLEN);
        !          1523:       mvaddstr (10, 1, "=> ");
        !          1524:
        !          1525:       if (error == 1)
        !          1526:         {
        !          1527:           mvaddstr (15, 1, "Sorry, the passwords don't match. Try again.");
        !          1528:           move (10, 4);
        !          1529:         }
        !          1530:
        !          1531:       refresh ();
        !          1532:
        !          1533:       if (mygetnstr (buf, DGL_PASSWDLEN, 0) != OK)
        !          1534:          return 0;
        !          1535:
        !          1536:       if (*buf == '\0')
        !          1537:         return 0;
        !          1538:
        !          1539:       if (strchr (buf, ':') != NULL) {
        !          1540:          debug_write("cannot have ':' in passwd");
        !          1541:         graceful_exit (112);
        !          1542:       }
        !          1543:
        !          1544:       mvaddstr (12, 1, "And again:");
        !          1545:       mvaddstr (13, 1, "=> ");
        !          1546:
        !          1547:       if (mygetnstr (repeatbuf, DGL_PASSWDLEN, 0) != OK)
        !          1548:          return 0;
        !          1549:
        !          1550:       if (!strcmp (buf, repeatbuf))
        !          1551:         error = 0;
        !          1552:       else
        !          1553:         error = 1;
        !          1554:     }
        !          1555:
        !          1556:   free(me->password);
        !          1557:   me->password = strdup (crypt (buf, buf));
        !          1558:
        !          1559:   if (dowrite)
        !          1560:     writefile (0);
        !          1561:
        !          1562:   return 1;
        !          1563: }
        !          1564:
        !          1565: /* ************************************************************* */
        !          1566:
        !          1567: void
        !          1568: wall_email(char *from, char *msg)
        !          1569: {
        !          1570:     int len, i;
        !          1571:     struct dg_game **games = NULL;
        !          1572:     char spool_fn[1024+1];
        !          1573:     FILE *user_spool = NULL;
        !          1574:     struct flock fl = { 0 };
        !          1575:
        !          1576:     fl.l_type = F_WRLCK;
        !          1577:     fl.l_whence = SEEK_SET;
        !          1578:     fl.l_start = 0;
        !          1579:     fl.l_len = 0;
        !          1580:
        !          1581:     if (!from || !msg) return;
        !          1582:
        !          1583:     if (strlen(from) < 1) {
        !          1584:        fprintf(stderr, "Error: wall: 'from' username is too short!\n");
        !          1585:        debug_write("wall: 'from' username too short");
        !          1586:        graceful_exit(121);
        !          1587:     }
        !          1588:
        !          1589:     if (strlen(msg) >= DGL_MAILMSGLEN) {
        !          1590:        fprintf(stderr, "Error: wall: message too long!\n");
        !          1591:        debug_write("wall: message too long");
        !          1592:        graceful_exit(120);
        !          1593:     }
        !          1594:
        !          1595:     games = populate_games(-1, &len, me);
        !          1596:
        !          1597:     if (len == 0) {
        !          1598:        fprintf(stderr, "Error: wall: no one's logged in!\n");
        !          1599:        debug_write("wall: no people playing");
        !          1600:        graceful_exit(118);
        !          1601:     }
        !          1602:
        !          1603:     for (i = 0; i < len; i++) {
        !          1604:        int game = games[i]->gamenum;
        !          1605:        int fnamelen;
        !          1606:        if (strlen(myconfig[game]->spool) < 1) continue;
        !          1607:
        !          1608:        snprintf (spool_fn, 1024, "%s/%s", myconfig[game]->spool, games[i]->name);
        !          1609:
        !          1610:        if ((user_spool = fopen (spool_fn, "a")) == NULL) continue;
        !          1611:
        !          1612:        while (fcntl(fileno(user_spool), F_SETLK, &fl) == -1) {
        !          1613:            if (errno != EAGAIN) continue;
        !          1614:            sleep (1);
        !          1615:        }
        !          1616:        fprintf(user_spool, "%s:%s\n", from, msg);
        !          1617:        fclose(user_spool);
        !          1618:     }
        !          1619:     free_populated_games(games, len);
        !          1620: }
        !          1621:
        !          1622: void
        !          1623: domailuser (char *username)
        !          1624: {
        !          1625:   unsigned int len, i;
        !          1626:   char *spool_fn, message[DGL_MAILMSGLEN+1];
        !          1627:   FILE *user_spool = NULL;
        !          1628:   time_t now;
        !          1629:   int mail_empty = 1;
        !          1630:   int game;
        !          1631:   struct flock fl = { 0 };
        !          1632:
        !          1633:   fl.l_type = F_WRLCK;
        !          1634:   fl.l_whence = SEEK_SET;
        !          1635:   fl.l_start = 0;
        !          1636:   fl.l_len = 0;
        !          1637:
        !          1638:   assert (loggedin);
        !          1639:
        !          1640:   game = 0; /*TODO: find_curr_player_game(username) */
        !          1641:
        !          1642:   if (strlen(myconfig[game]->spool) < 1) return;
        !          1643:
        !          1644:   len = strlen(myconfig[game]->spool) + strlen (username) + 1;
        !          1645:   spool_fn = malloc (len + 1);
        !          1646:   time (&now);
        !          1647:   snprintf (spool_fn, len + 1, "%s/%s", myconfig[game]->spool, username);
        !          1648:
        !          1649:   /* print the enter your message line */
        !          1650:   clear ();
        !          1651:   drawbanner (&banner);
        !          1652:   mvprintw (5, 1,
        !          1653:             "Enter your message here. It is to be one line only and %i characters or less.",
        !          1654:            DGL_MAILMSGLEN);
        !          1655:   mvaddstr (7, 1, "=> ");
        !          1656:
        !          1657:   if (mygetnstr (message, DGL_MAILMSGLEN, 1) != OK)
        !          1658:       return;
        !          1659:
        !          1660:   for (i = 0; i < strlen (message); i++)
        !          1661:     {
        !          1662:       if (message[i] != ' ' && message[i] != '\n' && message[i] != '\t')
        !          1663:         mail_empty = 0;
        !          1664:     }
        !          1665:
        !          1666:   if (mail_empty)
        !          1667:     {
        !          1668:       mvaddstr (9, 1, "This scroll appears to be blank.");
        !          1669:       mvaddstr (10, 1, "(Aborting your message.)");
        !          1670:       mvaddstr (12, 1, "--More--");
        !          1671:       dgl_getch ();
        !          1672:       return;
        !          1673:     }
        !          1674:
        !          1675:   if ((user_spool = fopen (spool_fn, "a")) == NULL)
        !          1676:     {
        !          1677:       mvaddstr (9, 1,
        !          1678:                 "You fall into the water!  You sink like a rock.");
        !          1679:       mvprintw (10, 1,
        !          1680:                 "(Couldn't open %s'%c spool file.  Aborting.)",
        !          1681:                 username, (username[strlen (username) - 1] != 's') ? 's' : 0);
        !          1682:       mvaddstr (12, 1, "--More--");
        !          1683:       dgl_getch ();
        !          1684:       return;
        !          1685:     }
        !          1686:
        !          1687:   mvaddstr (9, 1, "Sending your scroll...");
        !          1688:   refresh ();
        !          1689:
        !          1690:   /* Getting a lock on the mailspool... */
        !          1691:   while (fcntl (fileno (user_spool), F_SETLK, &fl) == -1)
        !          1692:     {
        !          1693:       if (errno != EAGAIN)
        !          1694:         {
        !          1695:           mvaddstr (10, 1,
        !          1696:                     "(Received a weird error from fcntl.  Aborting.)");
        !          1697:          mvaddstr (12, 1, "--More--");
        !          1698:           dgl_getch ();
        !          1699:           return;
        !          1700:         }
        !          1701:       sleep (1);
        !          1702:     }
        !          1703:
        !          1704:   fprintf (user_spool, "%s:%s\n", me->username, message);
        !          1705:
        !          1706:   /*
        !          1707:    * Don't unlock the file ourselves, this way it will be done automatically
        !          1708:    * after all data has been written. (Using file locking with stdio is icky.)
        !          1709:    */
        !          1710:
        !          1711:   fclose (user_spool);
        !          1712:
        !          1713:   mvaddstr (9, 1, "Scroll delivered!         ");
        !          1714:   move(9, 19); /* Pedantry! */
        !          1715:   refresh ();
        !          1716:   sleep (2);
        !          1717:
        !          1718:   return;
        !          1719: }
        !          1720:
        !          1721:
        !          1722: /* ************************************************************* */
        !          1723:
        !          1724: void
        !          1725: freefile ()
        !          1726: {
        !          1727: #ifndef USE_SQLITE3
        !          1728:   int i;
        !          1729:
        !          1730:   /* free existing mem, clear existing entries */
        !          1731:   for (i = 0; i < f_num; i++)
        !          1732:     {
        !          1733:       if (users[i] != me)
        !          1734:       {
        !          1735:        free (users[i]->password);
        !          1736:        free (users[i]->username);
        !          1737:        free (users[i]->email);
        !          1738:        free (users[i]->env);
        !          1739:        free (users[i]);
        !          1740:       }
        !          1741:     }
        !          1742:
        !          1743:   if (users)
        !          1744:     free (users);
        !          1745:
        !          1746:   users = NULL;
        !          1747:   f_num = 0;
        !          1748: #endif
        !          1749: }
        !          1750:
        !          1751: /* ************************************************************* */
        !          1752:
        !          1753: void
        !          1754: initcurses ()
        !          1755: {
        !          1756:   printf("\033[2J");
        !          1757:   if (newterm(NULL, stdout, stdin) == NULL) {
        !          1758:       if (!globalconfig.defterm || (newterm(globalconfig.defterm, stdout, stdin) == NULL)) {
        !          1759:          debug_write("cannot create newterm");
        !          1760:          graceful_exit(60);
        !          1761:       }
        !          1762:       mysetenv("TERM", globalconfig.defterm, 1);
        !          1763:   }
        !          1764:   cbreak ();
        !          1765:   noecho ();
        !          1766:   nonl ();
        !          1767:   intrflush (stdscr, FALSE);
        !          1768:   keypad (stdscr, TRUE);
        !          1769: #ifdef USE_NCURSES_COLOR
        !          1770:   start_color();
        !          1771:   use_default_colors();
        !          1772:
        !          1773:   init_pair(COLOR_BLACK, COLOR_WHITE, COLOR_BLACK);
        !          1774:   init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
        !          1775:   init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
        !          1776:   init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
        !          1777:   init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
        !          1778:   init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
        !          1779:   init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
        !          1780:   init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
        !          1781:   init_pair(9, 0, COLOR_BLACK);
        !          1782:   init_pair(10, COLOR_BLACK, COLOR_BLACK);
        !          1783:   init_pair(11, -1, -1);
        !          1784:
        !          1785:   if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
        !          1786: #endif
        !          1787:   clear();
        !          1788:   refresh();
        !          1789: }
        !          1790:
        !          1791: /* ************************************************************* */
        !          1792:
        !          1793: void
        !          1794: autologin (char* user, char *pass)
        !          1795: {
        !          1796:   struct dg_user *tmp;
        !          1797:   tmp = userexist(user, 0);
        !          1798:   if (tmp) {
        !          1799:       me = cpy_me(tmp);
        !          1800:       if ((passwordgood(pass) || initplayer == 1) && !(me->flags & DGLACCT_LOGIN_LOCK)) {
        !          1801:          loggedin = 1;
        !          1802:          setproctitle ("%s", me->username);
        !          1803:          dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_LOGIN], 0, me);
        !          1804:       }
        !          1805:   }
        !          1806: }
        !          1807:
        !          1808: void
        !          1809: loginprompt (int from_ttyplay)
        !          1810: {
        !          1811:   char user_buf[DGL_PLAYERNAMELEN+1], pw_buf[DGL_PASSWDLEN+2];
        !          1812:   int error = 2;
        !          1813:
        !          1814:   loggedin = 0;
        !          1815:
        !          1816:   while (error)
        !          1817:     {
        !          1818:       clear ();
        !          1819:
        !          1820:       drawbanner (&banner);
        !          1821:
        !          1822:       if (from_ttyplay == 1)
        !          1823:        mvaddstr (4, 1, "This operation requires you to be logged in.");
        !          1824:
        !          1825:       mvaddstr (5, 1,
        !          1826:                 "Please enter your username. (blank entry aborts)");
        !          1827:       mvaddstr (7, 1, "=> ");
        !          1828:
        !          1829:       if (error == 1)
        !          1830:         {
        !          1831:           mvaddstr (9, 1, "There was a problem with your last entry.");
        !          1832:           move (7, 4);
        !          1833:         }
        !          1834:
        !          1835:       refresh ();
        !          1836:
        !          1837:       if (mygetnstr (user_buf, DGL_PLAYERNAMELEN, 1) != OK)
        !          1838:          return;
        !          1839:
        !          1840:       if (*user_buf == '\0')
        !          1841:         return;
        !          1842:
        !          1843:       error = 1;
        !          1844:
        !          1845:       {
        !          1846:          struct dg_user *tmpme;
        !          1847:          if ((tmpme = userexist(user_buf, 0))) {
        !          1848:              me = cpy_me(tmpme);
        !          1849:              error = 0;
        !          1850:          }
        !          1851:       }
        !          1852:     }
        !          1853:
        !          1854:   clear ();
        !          1855:
        !          1856:   drawbanner (&banner);
        !          1857:
        !          1858:   mvaddstr (5, 1, "Please enter your password.");
        !          1859:   mvaddstr (7, 1, "=> ");
        !          1860:
        !          1861:   refresh ();
        !          1862:
        !          1863:   if (mygetnstr (pw_buf, DGL_PASSWDLEN, 0) != OK)
        !          1864:       return;
        !          1865:
        !          1866:   if (passwordgood (pw_buf))
        !          1867:     {
        !          1868:        if (me->flags & DGLACCT_LOGIN_LOCK) {
        !          1869:            clear ();
        !          1870:            mvprintw(5, 1, "Sorry, that account has been banned.--More--");
        !          1871:            dgl_getch();
        !          1872:            return;
        !          1873:        }
        !          1874:
        !          1875:       loggedin = 1;
        !          1876:       if (from_ttyplay)
        !          1877:          setproctitle("%s [watching %s]", me->username, chosen_name);
        !          1878:       else
        !          1879:          setproctitle("%s", me->username);
        !          1880:       dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_LOGIN], 0, me);
        !          1881:     }
        !          1882:   else
        !          1883:   {
        !          1884:     me = NULL;
        !          1885:     if (from_ttyplay == 1)
        !          1886:     {
        !          1887:       mvaddstr(9, 1, "Login failed. Returning to game.");
        !          1888:       refresh();
        !          1889:       sleep(2);
        !          1890:     }
        !          1891:   }
        !          1892: }
        !          1893:
        !          1894: /* ************************************************************* */
        !          1895:
        !          1896: void
        !          1897: newuser ()
        !          1898: {
        !          1899:   char buf[1024], dirname[100];
        !          1900:   int error = 2;
        !          1901:   unsigned int i;
        !          1902:
        !          1903:   loggedin = 0;
        !          1904:
        !          1905: #ifndef USE_SQLITE3
        !          1906:   if (f_num >= globalconfig.max)
        !          1907:   {
        !          1908:       clear ();
        !          1909:
        !          1910:       drawbanner (&banner);
        !          1911:
        !          1912:       mvaddstr (5, 1, "Sorry, too many users have registered now.");
        !          1913:       mvaddstr (6, 1, "You might email the server administrator.");
        !          1914:       mvaddstr (7, 1, "Press return to return to the menu. ");
        !          1915:       dgl_getch ();
        !          1916:
        !          1917:       return;
        !          1918:   }
        !          1919: #endif
        !          1920:
        !          1921:   if (me)
        !          1922:     free (me);
        !          1923:
        !          1924:   me = calloc (1, sizeof (struct dg_user));
        !          1925:
        !          1926:   while (error)
        !          1927:     {
        !          1928:       clear ();
        !          1929:
        !          1930:       sprintf(buf, "%i character max.", globalconfig.max_newnick_len);
        !          1931:
        !          1932:       drawbanner (&banner);
        !          1933:
        !          1934:       mvaddstr (5, 1, "Welcome new user. Please enter a username.");
        !          1935:       mvaddstr (6, 1,
        !          1936:                 "Only characters and numbers are allowed, with no spaces.");
        !          1937:       mvaddstr (7, 1, buf);
        !          1938:       mvaddstr (9, 1, "=> ");
        !          1939:
        !          1940:       if (error == 1)
        !          1941:         {
        !          1942:           mvaddstr (11, 1, "There was a problem with your last entry.");
        !          1943:           move (9, 4);
        !          1944:         }
        !          1945:
        !          1946:       refresh ();
        !          1947:
        !          1948:       if (mygetnstr (buf, globalconfig.max_newnick_len, 1) != OK)
        !          1949:          buf[0] = 0;
        !          1950:
        !          1951:       if (*buf == '\0') {
        !          1952:          free(me);
        !          1953:          me = NULL;
        !          1954:          return;
        !          1955:       }
        !          1956:
        !          1957:       if (!userexist(buf, 1)) {
        !          1958:          error = 0;
        !          1959:       } else
        !          1960:          error = 1;
        !          1961:
        !          1962:       for (i = 0; i < strlen (buf); i++)
        !          1963:         {
        !          1964:           if (!isalnum((int)buf[i]))
        !          1965:             error = 1;
        !          1966:         }
        !          1967:
        !          1968:       if (strlen (buf) < 2)
        !          1969:         error = 1;
        !          1970:
        !          1971:       if (strlen (buf) == 0)
        !          1972:       {
        !          1973:        free(me);
        !          1974:        me = NULL;
        !          1975:         return;
        !          1976:       }
        !          1977:     }
        !          1978:
        !          1979:   me->username = strdup (buf);
        !          1980:
        !          1981:   /* password step */
        !          1982:
        !          1983:   clear ();
        !          1984:
        !          1985:   if (!changepw (0))                  /* Calling changepw instead to prompt twice. */
        !          1986:   {
        !          1987:     free(me->username);
        !          1988:     free(me);
        !          1989:     me = NULL;
        !          1990:     return;
        !          1991:   }
        !          1992:
        !          1993:   /* email step */
        !          1994:
        !          1995:   error = 2;
        !          1996:   while (error != 0)
        !          1997:     {
        !          1998:       clear ();
        !          1999:
        !          2000:       drawbanner (&banner);
        !          2001:
        !          2002:       mvaddstr (5, 1, "Please enter your email address.");
        !          2003:       mvaddstr (6, 1, "This is sent _nowhere_ but will be used if you ask"
        !          2004:         " the sysadmin for lost");
        !          2005:       mvaddstr (7, 1, "password help. Please use a correct one. It only"
        !          2006:         " benefits you.");
        !          2007:       mvaddstr (8, 1, "80 character max. No ':' characters. Blank line"
        !          2008:         " aborts.");
        !          2009:       mvaddstr (10, 1, "=> ");
        !          2010:
        !          2011:       if (error == 1)
        !          2012:         {
        !          2013:           mvaddstr (12, 1, "There was a problem with your last entry.");
        !          2014:           move (10, 4);
        !          2015:         }
        !          2016:
        !          2017:       refresh ();
        !          2018:       if (mygetnstr (buf, 80, 1) != OK)
        !          2019:          buf[0] = 0;
        !          2020:
        !          2021:       if (check_email (buf))
        !          2022:         error = 0;
        !          2023:       else
        !          2024:         error = 1;
        !          2025:
        !          2026:       if (*buf == '\0')
        !          2027:       {
        !          2028:         free (me->username);
        !          2029:         free (me->password);
        !          2030:         free (me);
        !          2031:         me = NULL;
        !          2032:         return;
        !          2033:       }
        !          2034:     }
        !          2035:
        !          2036:   me->email = strdup (buf);
        !          2037:   me->env = calloc (1, 1);
        !          2038:   me->flags = 0;
        !          2039:
        !          2040:   loggedin = 1;
        !          2041:
        !          2042:   setproctitle ("%s", me->username);
        !          2043:
        !          2044:   dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_REGISTER], 0, me);
        !          2045:
        !          2046:   writefile (1);
        !          2047: }
        !          2048:
        !          2049: /* ************************************************************* */
        !          2050:
        !          2051: int
        !          2052: passwordgood (char *cpw)
        !          2053: {
        !          2054:   char *crypted;
        !          2055:   assert (me != NULL);
        !          2056:
        !          2057:   crypted = crypt (cpw, cpw);
        !          2058:   if (crypted == NULL)
        !          2059:       return 0;
        !          2060:   if (!strncmp (crypted, me->password, DGL_PASSWDLEN))
        !          2061:     return 1;
        !          2062:   if (!strncmp (cpw, me->password, DGL_PASSWDLEN))
        !          2063:     return 1;
        !          2064:
        !          2065:   return 0;
        !          2066: }
        !          2067:
        !          2068: /* ************************************************************* */
        !          2069:
        !          2070: int
        !          2071: readfile (int nolock)
        !          2072: {
        !          2073: #ifndef USE_SQLITE3
        !          2074:   FILE *fp = NULL, *fpl = NULL;
        !          2075:   char buf[1200];
        !          2076:   struct flock fl = { 0 };
        !          2077:
        !          2078:   fl.l_type = F_RDLCK;
        !          2079:   fl.l_whence = SEEK_SET;
        !          2080:   fl.l_start = 0;
        !          2081:   fl.l_len = 0;
        !          2082:
        !          2083:   memset (buf, 0, 1024);
        !          2084:
        !          2085:   /* read new stuff */
        !          2086:
        !          2087:   if (!nolock)
        !          2088:     {
        !          2089:       fpl = fopen (globalconfig.lockfile, "r");
        !          2090:       if (!fpl) {
        !          2091:          debug_write("cannot fopen lockfile");
        !          2092:         graceful_exit (106);
        !          2093:       }
        !          2094:       if (fcntl (fileno (fpl), F_SETLKW, &fl) == -1) {
        !          2095:          debug_write("cannot fcntl lockfile");
        !          2096:         graceful_exit (95);
        !          2097:       }
        !          2098:     }
        !          2099:
        !          2100:   fp = fopen (globalconfig.passwd, "r");
        !          2101:   if (!fp) {
        !          2102:       debug_write("cannot fopen passwd file");
        !          2103:     graceful_exit (106);
        !          2104:   }
        !          2105:
        !          2106:   /* once per name in the file */
        !          2107:   while (fgets (buf, 1200, fp))
        !          2108:     {
        !          2109:       char *b = buf, *n = buf;
        !          2110:
        !          2111:       users = realloc (users, sizeof (struct dg_user *) * (f_num + 1));
        !          2112:       users[f_num] = malloc (sizeof (struct dg_user));
        !          2113:       users[f_num]->username = (char *) calloc (DGL_PLAYERNAMELEN+2, sizeof (char));
        !          2114:       users[f_num]->email = (char *) calloc (82, sizeof (char));
        !          2115:       users[f_num]->password = (char *) calloc (DGL_PASSWDLEN+2, sizeof (char));
        !          2116:       users[f_num]->env = (char *) calloc (1026, sizeof (char));
        !          2117:
        !          2118:       /* name field, must be valid */
        !          2119:       while (*b != ':')
        !          2120:         {
        !          2121:           if (!isalnum((int)*b))
        !          2122:             return 1;
        !          2123:           users[f_num]->username[(b - n)] = *b;
        !          2124:           b++;
        !          2125:           if ((b - n) >= DGL_PLAYERNAMELEN) {
        !          2126:              debug_write("name field too long");
        !          2127:             graceful_exit (100);
        !          2128:          }
        !          2129:         }
        !          2130:
        !          2131:       /* advance to next field */
        !          2132:       n = b + 1;
        !          2133:       b = n;
        !          2134:
        !          2135:       /* email field */
        !          2136:       while (*b != ':')
        !          2137:         {
        !          2138:           users[f_num]->email[(b - n)] = *b;
        !          2139:           b++;
        !          2140:           if ((b - n) > 80) {
        !          2141:              debug_write("email field too long");
        !          2142:             graceful_exit (101);
        !          2143:          }
        !          2144:         }
        !          2145:
        !          2146:       /* advance to next field */
        !          2147:       n = b + 1;
        !          2148:       b = n;
        !          2149:
        !          2150:       /* pw field */
        !          2151:       while (*b != ':')
        !          2152:         {
        !          2153:           users[f_num]->password[(b - n)] = *b;
        !          2154:           b++;
        !          2155:           if ((b - n) >= DGL_PASSWDLEN) {
        !          2156:              debug_write("passwd field too long");
        !          2157:             graceful_exit (102);
        !          2158:          }
        !          2159:         }
        !          2160:
        !          2161:       /* advance to next field */
        !          2162:       n = b + 1;
        !          2163:       b = n;
        !          2164:
        !          2165:       /* env field */
        !          2166:       while ((*b != '\n') && (*b != 0) && (*b != EOF))
        !          2167:         {
        !          2168:           users[f_num]->env[(b - n)] = *b;
        !          2169:           b++;
        !          2170:           if ((b - n) >= 1024) {
        !          2171:              debug_write("env field too long");
        !          2172:             graceful_exit (103);
        !          2173:          }
        !          2174:         }
        !          2175:
        !          2176:       f_num++;
        !          2177:       /* prevent a buffer overrun here */
        !          2178:       if (f_num > globalconfig.max)
        !          2179:       {
        !          2180:        fprintf(stderr,"ERROR: number of users in database exceeds maximum. Exiting.\n");
        !          2181:        debug_write("too many users in database");
        !          2182:         graceful_exit (109);
        !          2183:       }
        !          2184:     }
        !          2185:
        !          2186:   if (!nolock)
        !          2187:       fclose (fpl);
        !          2188:   fclose (fp);
        !          2189: #endif
        !          2190:   return 0;
        !          2191: }
        !          2192:
        !          2193: /* ************************************************************* */
        !          2194:
        !          2195: #ifndef USE_SQLITE3
        !          2196: struct dg_user *userexist_tmp_me = NULL;
        !          2197:
        !          2198: struct dg_user *
        !          2199: userexist (char *cname, int isnew)
        !          2200: {
        !          2201:   int i;
        !          2202:
        !          2203:   if (userexist_tmp_me) {
        !          2204:       free(userexist_tmp_me->username);
        !          2205:       free(userexist_tmp_me->email);
        !          2206:       free(userexist_tmp_me->env);
        !          2207:       free(userexist_tmp_me->password);
        !          2208:       free(userexist_tmp_me);
        !          2209:       userexist_tmp_me = NULL;
        !          2210:   }
        !          2211:
        !          2212:   for (i = 0; i < f_num; i++)
        !          2213:     {
        !          2214:        if (!strncasecmp (cname, users[i]->username, (isnew ? globalconfig.max_newnick_len : DGL_PLAYERNAMELEN))) {
        !          2215:            userexist_tmp_me = cpy_me(users[i]);
        !          2216:            return userexist_tmp_me;
        !          2217:        }
        !          2218:     }
        !          2219:
        !          2220:   return NULL;
        !          2221: }
        !          2222: #else
        !          2223:
        !          2224: struct dg_user *userexist_tmp_me = NULL;
        !          2225:
        !          2226: static int
        !          2227: userexist_callback(void *NotUsed, int argc, char **argv, char **colname)
        !          2228: {
        !          2229:     int i;
        !          2230:     NotUsed = NULL;
        !          2231:
        !          2232:     userexist_tmp_me = malloc(sizeof(struct dg_user));
        !          2233:
        !          2234:     for (i = 0; i < argc; i++) {
        !          2235:        if (!strcmp(colname[i], "username"))
        !          2236:            userexist_tmp_me->username = strdup(argv[i]);
        !          2237:        else if (!strcmp(colname[i], "email"))
        !          2238:            userexist_tmp_me->email = strdup(argv[i]);
        !          2239:        else if (!strcmp(colname[i], "env"))
        !          2240:            userexist_tmp_me->env = strdup(argv[i]);
        !          2241:        else if (!strcmp(colname[i], "password"))
        !          2242:            userexist_tmp_me->password = strdup(argv[i]);
        !          2243:        else if (!strcmp(colname[i], "flags"))
        !          2244:            userexist_tmp_me->flags = atoi(argv[i]);
        !          2245:        else if (!strcmp(colname[i], "id"))
        !          2246:            userexist_tmp_me->id = atoi(argv[i]);
        !          2247:     }
        !          2248:     return 0;
        !          2249: }
        !          2250:
        !          2251: struct dg_user *
        !          2252: userexist (char *cname, int isnew)
        !          2253: {
        !          2254:     sqlite3 *db;
        !          2255:     char *errmsg = NULL;
        !          2256:     int ret, retry = 10;
        !          2257:
        !          2258:     char *qbuf;
        !          2259:
        !          2260:     char tmpbuf[DGL_PLAYERNAMELEN+2];
        !          2261:
        !          2262:     memset(tmpbuf, 0, DGL_PLAYERNAMELEN+2);
        !          2263:     strncpy(tmpbuf, cname, (isnew ? globalconfig.max_newnick_len : DGL_PLAYERNAMELEN));
        !          2264:
        !          2265:     /* Check that the nick doesn't interfere with already registered nicks */
        !          2266:     if (isnew && (strlen(cname) >= globalconfig.max_newnick_len))
        !          2267:        strcat(tmpbuf, "%");
        !          2268:
        !          2269:     qbuf = sqlite3_mprintf("select * from dglusers where username = '%q' collate nocase limit 1", tmpbuf);
        !          2270:
        !          2271:     ret = sqlite3_open(globalconfig.passwd, &db);
        !          2272:     if (ret) {
        !          2273:        sqlite3_close(db);
        !          2274:        debug_write("sqlite3_open failed");
        !          2275:        graceful_exit(96);
        !          2276:     }
        !          2277:
        !          2278:     if (userexist_tmp_me) {
        !          2279:        free(userexist_tmp_me->username);
        !          2280:        free(userexist_tmp_me->email);
        !          2281:        free(userexist_tmp_me->env);
        !          2282:        free(userexist_tmp_me->password);
        !          2283:        free(userexist_tmp_me);
        !          2284:        userexist_tmp_me = NULL;
        !          2285:     }
        !          2286:
        !          2287:     sqlite3_busy_timeout(db, 10000);
        !          2288:     ret = sqlite3_exec(db, qbuf, userexist_callback, 0, &errmsg);
        !          2289:
        !          2290:     sqlite3_free(qbuf);
        !          2291:
        !          2292:     if (ret != SQLITE_OK) {
        !          2293:        sqlite3_close(db);
        !          2294:        debug_write("sqlite3_exec failed");
        !          2295:        graceful_exit(108);
        !          2296:     }
        !          2297:     sqlite3_close(db);
        !          2298:
        !          2299:     return userexist_tmp_me;
        !          2300: }
        !          2301: #endif
        !          2302:
        !          2303: /* ************************************************************* */
        !          2304:
        !          2305: void
        !          2306: write_canned_rcfile (int game, char *target)
        !          2307: {
        !          2308:   FILE *canned, *newfile;
        !          2309:   char buf[1024], *rfn;
        !          2310:   size_t bytes, len;
        !          2311:
        !          2312:   len = strlen(myconfig[game]->rcfile) + 2;
        !          2313:   rfn = malloc(len);
        !          2314:   snprintf (rfn, len, "/%s", myconfig[game]->rcfile);
        !          2315:
        !          2316:   if (!(newfile = fopen (target, "w")))
        !          2317:     {
        !          2318:     bail:
        !          2319:       mvaddstr (13, 1,
        !          2320:                 "You don't know how to write that! You write \"%s was here\" and the scroll disappears.");
        !          2321:       mvaddstr (14, 1,
        !          2322:                 "(Sorry, but I couldn't open one of the config files. This is a bug.)");
        !          2323:       return;
        !          2324:     }
        !          2325:
        !          2326:   if (!(canned = fopen (rfn, "r")))
        !          2327:     goto bail;
        !          2328:
        !          2329:   free(rfn);
        !          2330:
        !          2331:   while ((bytes = fread (buf, 1, 1024, canned)) > 0)
        !          2332:     {
        !          2333:       if (fwrite (buf, 1, bytes, newfile) != bytes)
        !          2334:         {
        !          2335:           if (ferror (newfile))
        !          2336:             {
        !          2337:               mvaddstr (13, 1, "Your hand slips while engraving.");
        !          2338:               mvaddstr (14, 1,
        !          2339:                         "(Encountered a problem writing the new file. This is a bug.)");
        !          2340:               fclose (canned);
        !          2341:               fclose (newfile);
        !          2342:               return;
        !          2343:             }
        !          2344:         }
        !          2345:     }
        !          2346:
        !          2347:   fclose (canned);
        !          2348:   fclose (newfile);
        !          2349:   chmod (target, default_fmode);
        !          2350: }
        !          2351:
        !          2352:
        !          2353:
        !          2354: /* ************************************************************* */
        !          2355:
        !          2356: #ifndef USE_SQLITE3
        !          2357: void
        !          2358: writefile (int requirenew)
        !          2359: {
        !          2360:   FILE *fp, *fpl;
        !          2361:   int i = 0;
        !          2362:   int my_done = 0;
        !          2363:   struct flock fl = { 0 };
        !          2364:
        !          2365:   fl.l_type = F_WRLCK;
        !          2366:   fl.l_whence = SEEK_SET;
        !          2367:   fl.l_start = 0;
        !          2368:   fl.l_len = 0;
        !          2369:
        !          2370:   signals_block();
        !          2371:
        !          2372:   fpl = fopen (globalconfig.lockfile, "r+");
        !          2373:   if (!fpl)
        !          2374:     {
        !          2375:        signals_release();
        !          2376:       debug_write("writefile locking failed");
        !          2377:       graceful_exit (115);
        !          2378:     }
        !          2379:   if (fcntl (fileno (fpl), F_SETLK, &fl))
        !          2380:     {
        !          2381:        signals_release();
        !          2382:       debug_write("writefile fcntl failed");
        !          2383:       graceful_exit (107);
        !          2384:     }
        !          2385:
        !          2386:   fl.l_type = F_UNLCK;
        !          2387:
        !          2388:   freefile ();
        !          2389:   readfile (1);
        !          2390:
        !          2391:   fp = fopen (globalconfig.passwd, "w");
        !          2392:   if (!fp)
        !          2393:     {
        !          2394:        signals_release();
        !          2395:       debug_write("passwd file fopen failed");
        !          2396:       graceful_exit (99);
        !          2397:     }
        !          2398:
        !          2399:   for (i = 0; i < f_num; i++)
        !          2400:     {
        !          2401:       if (loggedin && !strncmp (me->username, users[i]->username, DGL_PLAYERNAMELEN))
        !          2402:         {
        !          2403:           if (requirenew)
        !          2404:             {
        !          2405:               /* this is if someone managed to register at the same time
        !          2406:                * as someone else. just die. */
        !          2407:              fclose(fp);
        !          2408:              fclose(fpl);
        !          2409:              signals_release();
        !          2410:              debug_write("two users registering at the same time");
        !          2411:               graceful_exit (111);
        !          2412:             }
        !          2413:           fprintf (fp, "%s:%s:%s:%s\n", me->username, me->email, me->password,
        !          2414:                    me->env);
        !          2415:           my_done = 1;
        !          2416:         }
        !          2417:       else
        !          2418:         {
        !          2419:           fprintf (fp, "%s:%s:%s:%s\n", users[i]->username, users[i]->email,
        !          2420:                    users[i]->password, users[i]->env);
        !          2421:         }
        !          2422:     }
        !          2423:   if (loggedin && !my_done)
        !          2424:     {                           /* new entry */
        !          2425:       if (f_num < globalconfig.max)
        !          2426:         fprintf (fp, "%s:%s:%s:%s\n", me->username, me->email, me->password,
        !          2427:                  me->env);
        !          2428:       else /* Oops, someone else registered the last available slot first */
        !          2429:        {
        !          2430:           fclose(fp);
        !          2431:          fclose(fpl);
        !          2432:          signals_release();
        !          2433:          debug_write("too many users in passwd db already");
        !          2434:           graceful_exit (116);
        !          2435:        }
        !          2436:     }
        !          2437:
        !          2438:   fclose (fp);
        !          2439:   fclose (fpl);
        !          2440:
        !          2441:   signals_release();
        !          2442: }
        !          2443: #else
        !          2444: void
        !          2445: writefile (int requirenew)
        !          2446: {
        !          2447:     sqlite3 *db;
        !          2448:     char *errmsg = NULL;
        !          2449:     int ret, retry = 10;
        !          2450:
        !          2451:     char *qbuf;
        !          2452:
        !          2453:     if (requirenew) {
        !          2454:        qbuf = sqlite3_mprintf("insert into dglusers (username, email, env, password, flags) values ('%q', '%q', '%q', '%q', %li)", me->username, me->email, me->env, me->password, me->flags);
        !          2455:     } else {
        !          2456:        qbuf = sqlite3_mprintf("update dglusers set username='%q', email='%q', env='%q', password='%q', flags=%li where id=%i", me->username, me->email, me->env, me->password, me->flags, me->id);
        !          2457:     }
        !          2458:
        !          2459:     ret = sqlite3_open(globalconfig.passwd, &db);
        !          2460:     if (ret) {
        !          2461:        sqlite3_close(db);
        !          2462:        debug_write("writefile sqlite3_open failed");
        !          2463:        graceful_exit(97);
        !          2464:     }
        !          2465:
        !          2466:     sqlite3_busy_timeout(db, 10000);
        !          2467:     ret = sqlite3_exec(db, qbuf, NULL, NULL, &errmsg);
        !          2468:
        !          2469:     sqlite3_free(qbuf);
        !          2470:
        !          2471:     if (ret != SQLITE_OK) {
        !          2472:        sqlite3_close(db);
        !          2473:        debug_write("writefile sqlite3_exec failed");
        !          2474:        graceful_exit(98);
        !          2475:     }
        !          2476:     sqlite3_close(db);
        !          2477: }
        !          2478: #endif
        !          2479:
        !          2480: /* ************************************************************* */
        !          2481:
        !          2482: int
        !          2483: purge_stale_locks (int game)
        !          2484: {
        !          2485:   DIR *pdir;
        !          2486:   struct dirent *dent;
        !          2487:   char* dir;
        !          2488:   size_t len;
        !          2489:   short firsttime = 1;
        !          2490:
        !          2491:   dir = strdup(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL));
        !          2492:
        !          2493:   if (!(pdir = opendir (dir))) {
        !          2494:       debug_write("purge_stale_locks dir open failed");
        !          2495:     graceful_exit (200);
        !          2496:   }
        !          2497:
        !          2498:   free(dir);
        !          2499:
        !          2500:   while ((dent = readdir (pdir)) != NULL)
        !          2501:     {
        !          2502:       FILE *ipfile;
        !          2503:       char *colon, *fn;
        !          2504:       char buf[16];
        !          2505:       pid_t pid;
        !          2506:       size_t len;
        !          2507:       int seconds = 0;
        !          2508:
        !          2509:       if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
        !          2510:         continue;
        !          2511:
        !          2512:       colon = strchr (dent->d_name, ':');
        !          2513:       /* should never happen */
        !          2514:       if (!colon) {
        !          2515:          debug_write("purge_stale_locks !colon");
        !          2516:         graceful_exit (201);
        !          2517:       }
        !          2518:       if (colon - dent->d_name != strlen(me->username))
        !          2519:         continue;
        !          2520:       if (strncmp (dent->d_name, me->username, colon - dent->d_name))
        !          2521:         continue;
        !          2522:
        !          2523:       len = strlen (dent->d_name) + strlen(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL)) + 1;
        !          2524:       fn = malloc (len);
        !          2525:
        !          2526:       snprintf (fn, len, "%s%s", dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL), dent->d_name);
        !          2527:
        !          2528:       if (!(ipfile = fopen (fn, "r"))) {
        !          2529:          debug_write("purge_stale_locks fopen inprogressdir fail");
        !          2530:         graceful_exit (202);
        !          2531:       }
        !          2532:
        !          2533:       if (fgets (buf, 16, ipfile) == NULL) {
        !          2534:          debug_write("purge_stale_locks fgets ipfile fail");
        !          2535:         graceful_exit (203);
        !          2536:       }
        !          2537:
        !          2538:       fclose (ipfile);
        !          2539:
        !          2540:       if (firsttime)
        !          2541:       {
        !          2542:        clear ();
        !          2543:        drawbanner (&banner);
        !          2544:
        !          2545: #define HUP_WAIT 10 /* seconds before HUPPING */
        !          2546:        mvprintw (3, 1,
        !          2547:            "There are some stale %s processes, will recover in %d  seconds.",
        !          2548:             myconfig[game]->game_name, HUP_WAIT);
        !          2549:        mvaddstr (4, 1,
        !          2550:            "Press a key NOW if you don't want this to happen!");
        !          2551:
        !          2552:        move (3, 51 + strlen(myconfig[game]->game_name)); /* pedantry */
        !          2553:        halfdelay(10);
        !          2554:
        !          2555:        for (seconds = HUP_WAIT - 1; seconds >= 0; seconds--)
        !          2556:        {
        !          2557:          if (dgl_getch() != ERR)
        !          2558:          {
        !          2559:            nocbreak(); /* leave half-delay */
        !          2560:            cbreak();
        !          2561:            return 0;
        !          2562:          }
        !          2563:          mvprintw (3, 50 + strlen(myconfig[game]->game_name), "%d%s", seconds, (seconds > 9) ? "" : " ");
        !          2564:        }
        !          2565:
        !          2566:        nocbreak();
        !          2567:        cbreak();
        !          2568:
        !          2569:        firsttime = 0;
        !          2570:       }
        !          2571:
        !          2572:       clear ();
        !          2573:       refresh ();
        !          2574:
        !          2575:       pid = atoi (buf);
        !          2576:
        !          2577:       kill (pid, SIGHUP);
        !          2578:
        !          2579:       errno = 0;
        !          2580:
        !          2581:       /* Wait for it to stop running */
        !          2582:       seconds = 0;
        !          2583:       while (kill (pid, 0) == 0)
        !          2584:         {
        !          2585:           seconds++;
        !          2586:           sleep (1);
        !          2587:           if (seconds == 10)
        !          2588:             {
        !          2589:               mvprintw (3, 1,
        !          2590:                         "Couldn't terminate one of your stale %s processes gracefully.", myconfig[game]->game_name);
        !          2591:               mvaddstr (4, 1, "Force its termination? [yn] ");
        !          2592:               if (tolower (dgl_getch ()) == 'y')
        !          2593:                 {
        !          2594:                   kill (pid, SIGTERM);
        !          2595:                   break;
        !          2596:                 }
        !          2597:               else
        !          2598:                 {
        !          2599:                   endwin ();
        !          2600:                   fprintf (stderr, "Sorry, no %s for you now, please "
        !          2601:                            "contact the admin.\n", myconfig[game]->game_name);
        !          2602:                  debug_write("could not terminate stale processes");
        !          2603:                   graceful_exit (1);
        !          2604:                 }
        !          2605:             }
        !          2606:         }
        !          2607:
        !          2608:       /* Don't remove the lock file until the process is dead. */
        !          2609:       unlink (fn);
        !          2610:       free (fn);
        !          2611:     }
        !          2612:
        !          2613:   closedir (pdir);
        !          2614:   return 1;
        !          2615: }
        !          2616:
        !          2617:
        !          2618: int
        !          2619: runmenuloop(struct dg_menu *menu)
        !          2620: {
        !          2621:     struct dg_banner ban;
        !          2622:     struct dg_menuoption *tmpopt;
        !          2623:     int userchoice = 0;
        !          2624:     int doclear = 1;
        !          2625:
        !          2626:     if (!menu) return 1;
        !          2627:
        !          2628:     ban.lines = NULL;
        !          2629:     ban.len = 0;
        !          2630:
        !          2631:     loadbanner(menu->banner_fn, &ban);
        !          2632:     while (1) {
        !          2633:        term_resize_check();
        !          2634:        if (doclear) {
        !          2635:            doclear = 0;
        !          2636:            if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
        !          2637:            clear();
        !          2638:        }
        !          2639:        drawbanner(&ban);
        !          2640:        if (menu->cursor_x >= 0 && menu->cursor_y >= 0)
        !          2641:            mvprintw(menu->cursor_y, menu->cursor_x, "");
        !          2642:        refresh();
        !          2643:        userchoice = dgl_getch();
        !          2644:        if (userchoice == ERR) {
        !          2645:            freebanner(&ban);
        !          2646:            return 1;
        !          2647:        }
        !          2648:        tmpopt = menu->options;
        !          2649:        while (tmpopt) {
        !          2650:            if (strchr(tmpopt->keys, userchoice)) {
        !          2651:                dgl_exec_cmdqueue(tmpopt->cmdqueue, selected_game, me);
        !          2652:                doclear = 1;
        !          2653:                break;
        !          2654:            } else {
        !          2655:                tmpopt = tmpopt->next;
        !          2656:            }
        !          2657:        }
        !          2658:
        !          2659:        if (return_from_submenu) {
        !          2660:            freebanner(&ban);
        !          2661:            return_from_submenu = 0;
        !          2662:            return 0;
        !          2663:        }
        !          2664:
        !          2665:        if (check_retard(0)) {
        !          2666:            freebanner(&ban);
        !          2667:            debug_write("retard");
        !          2668:            graceful_exit(119);
        !          2669:        }
        !          2670:     }
        !          2671: }
        !          2672:
        !          2673: int
        !          2674: main (int argc, char** argv)
        !          2675: {
        !          2676:   /* for chroot and program execution */
        !          2677:     char atrcfilename[81], *p, *auth = NULL;
        !          2678:   unsigned int len;
        !          2679:   int c, i;
        !          2680:   int userchoice;
        !          2681:   char *tmp;
        !          2682:   char *wall_email_str = NULL;
        !          2683: #ifdef USE_RLIMIT
        !          2684:   struct rlimit lim;
        !          2685: #endif
        !          2686:
        !          2687: #ifndef HAVE_SETPROCTITLE
        !          2688:   /* save argc, argv */
        !          2689:   char** saved_argv;
        !          2690:   int saved_argc;
        !          2691:
        !          2692:   saved_argc = argc;
        !          2693:
        !          2694:   saved_argv = malloc(sizeof(char**) * (argc + 1));
        !          2695:   for (i = 0; i < argc; i++)
        !          2696:     saved_argv[i] = strdup(argv[i]);
        !          2697:   saved_argv[i] = '\0';
        !          2698:
        !          2699:   compat_init_setproctitle(argc, argv);
        !          2700:   argv = saved_argv;
        !          2701: #endif
        !          2702:
        !          2703:   p = getenv("DGLAUTH");
        !          2704:
        !          2705:   /* Linux telnetd allows importing the USER envvar via telnet,
        !          2706:    * while FreeBSD does not.  FreeBSD, on the other hand, does allow
        !          2707:    * the LOGNAME envvar.  Check USER first, then LOGNAME.
        !          2708:    */
        !          2709:   if (p == NULL) {
        !          2710:       p = getenv("USER");
        !          2711:   }
        !          2712:
        !          2713:   if (p == NULL) {
        !          2714:       p = getenv("LOGNAME");
        !          2715:   }
        !          2716:
        !          2717:   if (p && *p != '\0')
        !          2718:     auth = strdup(p);
        !          2719:   /* else auth is still NULL */
        !          2720:
        !          2721:   /* just to be sure */
        !          2722:   unsetenv("DGLAUTH"); unsetenv("USER"); unsetenv("LOGNAME");
        !          2723:
        !          2724:   __progname = basename(strdup(argv[0]));
        !          2725:
        !          2726:   while ((c = getopt(argc, argv, "csqh:pi:aeW:SD")) != -1)
        !          2727:   {
        !          2728:       /* Stop processing arguments at -c, so that user-provided
        !          2729:        * commands (via ssh for example) to the dgamelaunch login
        !          2730:        * shell are ignored.
        !          2731:        */
        !          2732:     if (c == 'c') break;
        !          2733:     switch (c)
        !          2734:     {
        !          2735:       case 's':
        !          2736:        showplayers = 1; break;
        !          2737:
        !          2738:       case 'q':
        !          2739:        silent = 1; break;
        !          2740:
        !          2741:       case 'i':
        !          2742:        if (optarg && *optarg != '\0') {
        !          2743:                if (p && *p != '\0')
        !          2744:                        *p = '\0';
        !          2745:
        !          2746:                p = strdup(optarg);
        !          2747:                initplayer = 1;
        !          2748:
        !          2749:                if (auth && *auth != '\0')
        !          2750:                        *auth = '\0';
        !          2751:        }
        !          2752:        break;
        !          2753:
        !          2754:     case 'W':
        !          2755:        wall_email_str = strdup(optarg);
        !          2756:        break;
        !          2757:
        !          2758:     case 'S': /* Free the shared memory block */
        !          2759: #ifdef USE_SHMEM
        !          2760:        if (shm_free()) {
        !          2761:            if (!silent) fprintf(stderr, "nonexistent shmem block.\n");
        !          2762:        } else {
        !          2763:            if (!silent) fprintf(stderr, "shmem block freed.\n");
        !          2764:        }
        !          2765: #else
        !          2766:        if (!silent) fprintf(stderr, "warning: dgamelaunch was compiled without shmem.\n");
        !          2767: #endif
        !          2768:        graceful_exit(0);
        !          2769:        break;
        !          2770:
        !          2771:     case 'D': /* dump the shared memory block data */
        !          2772: #ifdef USE_SHMEM
        !          2773:        shm_dump();
        !          2774: #else
        !          2775:        if (!silent) fprintf(stderr, "warning: dgamelaunch was compiled without shmem.\n");
        !          2776: #endif
        !          2777:        graceful_exit(0);
        !          2778:        break;
        !          2779:
        !          2780:       default:
        !          2781:        break; /*ignore */
        !          2782:     }
        !          2783:   }
        !          2784:
        !          2785:   while (optind < argc)
        !          2786:   {
        !          2787:     size_t len = strlen(argv[optind]);
        !          2788:     memset(argv[optind++], 0, len);
        !          2789:   }
        !          2790:   setproctitle("<Anonymous>");
        !          2791:
        !          2792:   srand(time(0));
        !          2793:
        !          2794:   create_config();
        !          2795:
        !          2796:   /* signal handlers */
        !          2797:   signal (SIGHUP, catch_sighup);
        !          2798:   signal (SIGINT, catch_sighup);
        !          2799:   signal (SIGQUIT, catch_sighup);
        !          2800:   signal (SIGTERM, catch_sighup);
        !          2801:
        !          2802:   (void) tcgetattr (0, &tt);
        !          2803:
        !          2804:   if (!globalconfig.flowctrl) {
        !          2805:       tt.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF */
        !          2806:       (void) tcsetattr(0, TCSANOW, &tt);
        !          2807:   }
        !          2808:
        !          2809:   if (-1 == ioctl (0, TIOCGWINSZ, (char *) &win) || win.ws_row < 4 ||
        !          2810:                  win.ws_col < 4) /* Rudimentary validity check */
        !          2811:     {
        !          2812:       win.ws_row = 24;
        !          2813:       win.ws_col = 80;
        !          2814:       win.ws_xpixel = win.ws_col * 8;
        !          2815:       win.ws_ypixel = win.ws_row * 8;
        !          2816:     }
        !          2817:
        !          2818:   /* get master tty just before chroot (lives in /dev) */
        !          2819:   ttyrec_getpty ();
        !          2820:
        !          2821: #ifdef USE_RLIMIT
        !          2822: #ifdef USE_RLIMIT_CORE
        !          2823:   /* enable and set core dump size */
        !          2824:   if (!getrlimit(RLIMIT_CORE, &lim)) {
        !          2825:       lim.rlim_cur = USE_RLIMIT_CORE;
        !          2826:       setrlimit(RLIMIT_CORE, &lim);
        !          2827:   }
        !          2828: #endif
        !          2829: #ifdef USE_RLIMIT_AS
        !          2830:   /* set maximum memory usage */
        !          2831:   if (!getrlimit(RLIMIT_AS, &lim)) {
        !          2832:       lim.rlim_cur = USE_RLIMIT_AS;
        !          2833:       setrlimit(RLIMIT_AS, &lim);
        !          2834:   }
        !          2835: #endif
        !          2836: #endif
        !          2837:
        !          2838:   if (geteuid () != globalconfig.shed_uid)
        !          2839:     {
        !          2840:       /* chroot */
        !          2841:       if (chroot (globalconfig.chroot))
        !          2842:        {
        !          2843:          perror ("cannot change root directory");
        !          2844:          graceful_exit (2);
        !          2845:        }
        !          2846:
        !          2847:       if (chdir ("/"))
        !          2848:        {
        !          2849:          perror ("cannot chdir to root directory");
        !          2850:          graceful_exit (3);
        !          2851:        }
        !          2852:
        !          2853:       /* shed privs. this is done immediately after chroot. */
        !          2854:       if (setgroups (1, &globalconfig.shed_gid) == -1)
        !          2855:        {
        !          2856:          perror ("setgroups");
        !          2857:          graceful_exit (4);
        !          2858:        }
        !          2859:
        !          2860:       if (setgid (globalconfig.shed_gid) == -1)
        !          2861:        {
        !          2862:          perror ("setgid");
        !          2863:          graceful_exit (5);
        !          2864:        }
        !          2865:
        !          2866:       if (setuid (globalconfig.shed_uid) == -1)
        !          2867:        {
        !          2868:          perror ("setuid");
        !          2869:          graceful_exit (6);
        !          2870:        }
        !          2871:     }
        !          2872:
        !          2873:   if (globalconfig.locale) {
        !          2874:       setlocale(LC_CTYPE, globalconfig.locale);
        !          2875:   }
        !          2876:
        !          2877:   if (showplayers) {
        !          2878:     inprogressdisplay(-1);
        !          2879:     graceful_exit (0);
        !          2880:   }
        !          2881:
        !          2882:   if (wall_email_str) {
        !          2883:       char *emailfrom = wall_email_str;
        !          2884:       char *emailmsg = strchr(wall_email_str, ':');
        !          2885:       if (!emailmsg) {
        !          2886:          debug_write("wall: no mail msg");
        !          2887:          graceful_exit(117);
        !          2888:       }
        !          2889:       *emailmsg = '\0';
        !          2890:       emailmsg++;
        !          2891:       if (emailmsg)
        !          2892:          wall_email(emailfrom, emailmsg);
        !          2893:       graceful_exit(0);
        !          2894:   }
        !          2895:
        !          2896:   banner.len = 0;
        !          2897:   banner.lines = NULL;
        !          2898:   loadbanner(globalconfig.banner, &banner);
        !          2899:
        !          2900:   dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_DGLSTART], 0, NULL);
        !          2901:
        !          2902:   if (initplayer) {
        !          2903:          char *user, *pass;
        !          2904:
        !          2905:          user = strdup(p);
        !          2906:          pass = strdup(p);
        !          2907:
        !          2908:          autologin(user, pass);
        !          2909:
        !          2910:          if (loggedin) {
        !          2911:                dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_REGISTER], 0, me);
        !          2912:                fprintf(stdout, "Setup of %s succeeded.\n", me->username);
        !          2913:                graceful_exit(0);
        !          2914:          }
        !          2915:          else {
        !          2916:                fprintf(stdout, "Setup of %s failed.\n", p);
        !          2917:                graceful_exit(10);
        !          2918:          }
        !          2919:   }
        !          2920:
        !          2921:   /* simple login routine, uses ncurses */
        !          2922:   if (readfile (0)) {
        !          2923:       debug_write("log in fail");
        !          2924:     graceful_exit (110);
        !          2925:   }
        !          2926:
        !          2927:   if (auth)
        !          2928:   {
        !          2929:     char *user, *pass, *p;
        !          2930:
        !          2931:     p = strchr(auth, ':');
        !          2932:
        !          2933:     if (p)
        !          2934:     {
        !          2935:       pass = p + 1;
        !          2936:
        !          2937:       if (*pass != '\0')
        !          2938:       {
        !          2939:         *p = '\0';
        !          2940:         user = auth;
        !          2941:         autologin(user, pass);
        !          2942:       }
        !          2943:     }
        !          2944:   }
        !          2945:
        !          2946:   initcurses ();
        !          2947:
        !          2948:   g_chain_winch = signal(SIGWINCH, sigwinch_func);
        !          2949:
        !          2950:   term_resize_check();
        !          2951:
        !          2952:   idle_alarm_set_enabled(1);
        !          2953:
        !          2954:   while (1) {
        !          2955:       if (runmenuloop(dgl_find_menu(get_mainmenu_name())))
        !          2956:          break;
        !          2957:   }
        !          2958:
        !          2959:   idle_alarm_set_enabled(0);
        !          2960:
        !          2961:   /* NOW we can safely kill this */
        !          2962:   freefile ();
        !          2963:
        !          2964:   if (me)
        !          2965:     free (me);
        !          2966:
        !          2967:   freebanner(&banner);
        !          2968:   banner_var_free();
        !          2969:   graceful_exit (20);
        !          2970:
        !          2971:   return 1;
        !          2972: }

CVSweb