home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / x / volume21 / xmcd / part06 < prev    next >
Encoding:
Text File  |  1993-12-19  |  61.9 KB  |  2,872 lines

  1. Newsgroups: comp.sources.x
  2. From: ti@bazooka.amb.org (Ti Kan)
  3. Subject: v21i068:  xmcd - X11/Motif CD audio player, Part06/13
  4. Message-ID: <1993Dec19.193917.24240@sparky.sterling.com>
  5. X-Md4-Signature: 3d70787591fb8cce078e19cc63267b5c
  6. Sender: chris@sparky.sterling.com (Chris Olson)
  7. Organization: Sterling Software
  8. Date: Sun, 19 Dec 1993 19:39:17 GMT
  9. Approved: chris@sterling.com
  10.  
  11. Submitted-by: ti@bazooka.amb.org (Ti Kan)
  12. Posting-number: Volume 21, Issue 68
  13. Archive-name: xmcd/part06
  14. Environment: X11, OSF/Motif
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then unpack
  18. # it by saving it into a file and typing "sh file".  To overwrite existing
  19. # files, type "sh file -c".  You can also feed this as standard input via
  20. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  21. # will see the following message at the end:
  22. #        "End of archive 6 (of 13)."
  23. # Contents:  dbprog.c
  24. # Wrapped by ti@bazooka on Mon Nov  8 10:35:20 1993
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'dbprog.c' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'dbprog.c'\"
  28. else
  29. echo shar: Extracting \"'dbprog.c'\" \(58201 characters\)
  30. sed "s/^X//" >'dbprog.c' <<'END_OF_FILE'
  31. X/*
  32. X *   xmcd - Motif(tm) CD Audio Player
  33. X *
  34. X *   Copyright (C) 1993  Ti Kan
  35. X *   E-mail: ti@amb.org
  36. X *
  37. X *   This program is free software; you can redistribute it and/or modify
  38. X *   it under the terms of the GNU General Public License as published by
  39. X *   the Free Software Foundation; either version 2 of the License, or
  40. X *   (at your option) any later version.
  41. X *
  42. X *   This program is distributed in the hope that it will be useful,
  43. X *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  44. X *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  45. X *   GNU General Public License for more details.
  46. X *
  47. X *   You should have received a copy of the GNU General Public License
  48. X *   along with this program; if not, write to the Free Software
  49. X *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  50. X *
  51. X */
  52. X#ifndef LINT
  53. Xstatic char *_dbprog_c_ident_ = "@(#)dbprog.c    1.91 93/09/28";
  54. X#endif
  55. X
  56. X#include <Xm/Xm.h>
  57. X#include <Xm/Text.h>
  58. X#include <Xm/List.h>
  59. X#include <X11/Xatom.h>
  60. X#include "xmcd.h"
  61. X#include "widget.h"
  62. X#include "cdlib.h"
  63. X#include "cdfunc.h"
  64. X#include "util.h"
  65. X#include "patchlevel.h"
  66. X#include "dbprog.h"
  67. X
  68. X
  69. Xtypedef struct database {
  70. X    word32_t    discid;            /* Magic disc ID */
  71. X    byte_t        ntrk;            /* Number of disc tracks */
  72. X    char        *dbfile;        /* Path to database file */
  73. X    char        *dtitle;        /* Disc title */
  74. X    char        *trklist[MAXTRACK];    /* Track title list */
  75. X    char        *extd;            /* Extended disc info */
  76. X    char        *extt[MAXTRACK];    /* Extended track info */
  77. X    char        *sav_extt[MAXTRACK];    /* Bkup extended track info */
  78. X    char        *playorder;        /* Track play order */
  79. X} database_t;
  80. X
  81. X
  82. Xtypedef struct linkopts {
  83. X    char        *dtitle;        /* Disc title */
  84. X    char        idstr[9];        /* Disc id string */
  85. X    struct linkopts    *next;            /* Link to next item */
  86. X} linkopts_t;
  87. X
  88. X
  89. Xextern widgets_t    widgets;
  90. Xextern AppData        app_data;
  91. Xextern uid_t        ouid;
  92. Xextern char        **dbdirs;
  93. X
  94. XSTATIC char        timemode = TIME_TOTAL;    /* Time display mode flag */
  95. XSTATIC int        sel_pos = -1,        /* Track list select position */
  96. X            osel_pos = -1,        /* Old track list select pos */
  97. X            linksel_pos = -1,    /* Link select position */
  98. X            extt_pos = -1,        /* Ext track info position */
  99. X            dirsel_mode = DIRSEL_SAVE;
  100. X                        /* Directory selector mode */
  101. XSTATIC bool_t        title_edited = FALSE,    /* Track title edited flag */
  102. X            extt_setup = FALSE,    /* Ext track info setup */
  103. X            pgmseq_editing = FALSE,    /* Pgm seq edited flag */
  104. X            dbprog_changed = FALSE,    /* Flag to indicate change */
  105. X            extd_manage = FALSE,    /* Whether to manage extd */
  106. X            extt_manage = FALSE;    /* Whether to manage extt */
  107. XSTATIC database_t    cur_db;            /* Database entry of CD */
  108. XSTATIC linkopts_t    *linkhead = NULL;    /* Link options list head */
  109. X
  110. X#define MIN(a,b)    (((a) > (b)) ? (b) : (a))
  111. X#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
  112. X
  113. X
  114. X/*
  115. X * dbprog_strcat
  116. X *    Concatenate two text strings with special handling for newline
  117. X *    and tab character translations.
  118. X *
  119. X * Args:
  120. X *    s1 - The first text string and destination string.
  121. X *    s2 - The second text string.
  122. X *
  123. X * Return:
  124. X *    Pointer to the resultant string, or NULL if failed.
  125. X */
  126. XSTATIC char *
  127. Xdbprog_strcat(char *s1, char *s2)
  128. X{
  129. X    char    *cp = s1;
  130. X
  131. X    if (s1 == NULL || s2 == NULL)
  132. X        return(NULL);
  133. X
  134. X    /* Concatenate two strings, with special handling for newline
  135. X     * and tab characters.
  136. X     */
  137. X    for (s1 += strlen(s1); *s2 != '\0'; s1++, s2++) {
  138. X        if (*s2 == '\\') {
  139. X            switch (*(s2 + 1)) {
  140. X            case 'n':
  141. X                *s1 = '\n';
  142. X                s2++;
  143. X                break;
  144. X            case 't':
  145. X                *s1 = '\t';
  146. X                s2++;
  147. X                break;
  148. X            default:
  149. X                *s1 = *s2;
  150. X                break;
  151. X            }
  152. X        }
  153. X        else
  154. X            *s1 = *s2;
  155. X    }
  156. X    *s1 = '\0';
  157. X
  158. X    return(cp);
  159. X}
  160. X
  161. X
  162. X/*
  163. X * dbprog_sum
  164. X *    Convert an integer to its text string representation, and
  165. X *    compute its checksum.  Used by dbprog_discid to derive the
  166. X *    disc ID.
  167. X *
  168. X * Args:
  169. X *    n - The integer value.
  170. X *
  171. X * Return:
  172. X *    The integer checksum.
  173. X */
  174. XSTATIC int
  175. Xdbprog_sum(int n)
  176. X{
  177. X    char    buf[12],
  178. X        *p;
  179. X    int    ret = 0;
  180. X
  181. X    /* For backward compatibility this algorithm must not change */
  182. X    sprintf(buf, "%lu", n);
  183. X    for (p = buf; *p != '\0'; p++)
  184. X        ret += (*p - '0');
  185. X
  186. X    return(ret);
  187. X}
  188. X
  189. X
  190. X/*
  191. X * dbprog_discid
  192. X *    Compute a magic disc ID based on the number of tracks,
  193. X *    the length of each track, and a checksum of the string
  194. X *    that represents the offset of each track.
  195. X *
  196. X * Args:
  197. X *    s - Pointer to the curstat_t structure.
  198. X *
  199. X * Return:
  200. X *    The integer disc ID.
  201. X */
  202. XSTATIC word32_t
  203. Xdbprog_discid(curstat_t *s)
  204. X{
  205. X    int    i,
  206. X        t = 0,
  207. X        n = 0;
  208. X
  209. X    /* For backward compatibility this algorithm must not change */
  210. X    for (i = 0; i < (int) s->tot_trks; i++) {
  211. X        n += dbprog_sum((s->trkinfo[i].min * 60) + s->trkinfo[i].sec);
  212. X
  213. X        t += ((s->trkinfo[i+1].min * 60) + s->trkinfo[i+1].sec) -
  214. X             ((s->trkinfo[i].min * 60) + s->trkinfo[i].sec);
  215. X    }
  216. X
  217. X    return((n % 0xff) << 24 | t << 8 | s->tot_trks);
  218. X}
  219. X
  220. X
  221. X/*
  222. X * dbprog_parse
  223. X *    Parse the shuffle/program mode play sequence text string, and
  224. X *    update the playorder table in the curstat_t structure.
  225. X *
  226. X * Args:
  227. X *    s - Pointer to the curstat_t structure.
  228. X *
  229. X * Return:
  230. X *    TRUE=success, FALSE=error.
  231. X */
  232. XSTATIC bool_t
  233. Xdbprog_parse(curstat_t *s)
  234. X{
  235. X    int    i,
  236. X        j,
  237. X        n;
  238. X    char    *p,
  239. X        *q,
  240. X        *tmpbuf;
  241. X    bool_t    last = FALSE;
  242. X
  243. X    if (cur_db.playorder == NULL)
  244. X        return(FALSE);
  245. X
  246. X    n = strlen(cur_db.playorder) + 1;
  247. X    if ((tmpbuf = (char *) MEM_ALLOC(n)) == NULL) {
  248. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  249. X        return(FALSE);
  250. X    }
  251. X
  252. X    strcpy(tmpbuf, cur_db.playorder);
  253. X
  254. X    s->prog_tot = 0;
  255. X
  256. X    for (i = 0, p = q = tmpbuf; i < MAXTRACK; i++, p = ++q) {
  257. X        /* Skip p to the next digit */
  258. X        for (; !isdigit(*p) && *p != '\0'; p++)
  259. X            ;
  260. X
  261. X        if (*p == '\0')
  262. X            /* No more to do */
  263. X            break;
  264. X
  265. X        /* Skip q to the next non-digit */
  266. X        for (q = p; isdigit(*q); q++)
  267. X            ;
  268. X
  269. X        if (*q == PGM_SEPCHAR)
  270. X            *q = '\0';
  271. X        else if (*q == '\0')
  272. X            last = TRUE;
  273. X        else {
  274. X            MEM_FREE(tmpbuf);
  275. X            return(FALSE);
  276. X        }
  277. X
  278. X        if (q > p) {
  279. X            /* Update play sequence */
  280. X            for (j = 0; j < MAXTRACK; j++) {
  281. X                if (s->trkinfo[j].trkno == atoi(p)) {
  282. X                    s->playorder[i] = j;
  283. X                    s->prog_tot++;
  284. X                    break;
  285. X                }
  286. X            }
  287. X
  288. X            if (j >= MAXTRACK) {
  289. X                MEM_FREE(tmpbuf);
  290. X                return(FALSE);
  291. X            }
  292. X        }
  293. X
  294. X        if (last)
  295. X            break;
  296. X    }
  297. X
  298. X    if (s->prog_tot > 0)
  299. X        s->program = TRUE;
  300. X
  301. X    MEM_FREE(tmpbuf);
  302. X    return(TRUE);
  303. X}
  304. X
  305. X
  306. X/*
  307. X * dbprog_listupd
  308. X *    Update the track list display to reflect the contents of
  309. X *    the trkinfo table in the curstat_t structure.
  310. X *
  311. X * Args:
  312. X *    s - Pointer to the curstat_t structure.
  313. X *
  314. X * Return:
  315. X *    Nothing.
  316. X */
  317. XSTATIC void
  318. Xdbprog_listupd(curstat_t *s)
  319. X{
  320. X    int        i,
  321. X            n,
  322. X            secs;
  323. X    char        *str;
  324. X    byte_t        min,
  325. X            sec;
  326. X    XmString    xs;
  327. X
  328. X
  329. X    for (i = 0; i < (int) s->tot_trks; i++) {
  330. X        if (timemode == TIME_TOTAL) {
  331. X            min = (byte_t) s->trkinfo[i].min;
  332. X            sec = (byte_t) s->trkinfo[i].sec;
  333. X        }
  334. X        else {
  335. X            secs = ((s->trkinfo[i+1].min * 60 +
  336. X                s->trkinfo[i+1].sec) - 
  337. X                    (s->trkinfo[i].min * 60 +
  338. X                s->trkinfo[i].sec));
  339. X            min = (byte_t) (secs / 60);
  340. X            sec = (byte_t) (secs % 60);
  341. X        }
  342. X
  343. X        n = strlen((cur_db.trklist[i] == NULL) ?
  344. X               UNDEF_STR : cur_db.trklist[i]) + TRKLIST_PFLEN;
  345. X
  346. X        if ((str = (char *) MEM_ALLOC(n)) == NULL) {
  347. X            cd_fatal_popup(
  348. X                app_data.str_fatal,
  349. X                app_data.str_nomemory
  350. X            );
  351. X            return;
  352. X        }
  353. X
  354. X        if (cur_db.trklist[i] != NULL)
  355. X            sprintf(str, TRKLIST_FMT, s->trkinfo[i].trkno,
  356. X                min, sec, cur_db.trklist[i]);
  357. X        else
  358. X            sprintf(str, TRKLIST_FMT, s->trkinfo[i].trkno,
  359. X                min, sec, UNDEF_STR);
  360. X
  361. X        if (s->mode != M_NODISC && s->cur_trk >= 0 &&
  362. X            curtrk_pos(s) == i)
  363. X            xs = XmStringCreate(str, CHSET2);
  364. X        else
  365. X            xs = XmStringCreate(str, CHSET1);
  366. X
  367. X        XmListAddItemUnselected(widgets.dbprog.trk_list, xs, i + 1);
  368. X
  369. X        XmStringFree(xs);
  370. X        MEM_FREE(str);
  371. X    }
  372. X
  373. X    if (sel_pos > 0)
  374. X        /* This item is previously selected */
  375. X        XmListSelectPos(widgets.dbprog.trk_list, sel_pos, False);
  376. X}
  377. X
  378. X
  379. X/*
  380. X * dbprog_structupd
  381. X *    Update the cur_db structure to match the state of the various
  382. X *    input fields in the database/program window.
  383. X *
  384. X * Args:
  385. X *    s - Pointer to the curstat_t structure.
  386. X *
  387. X * Return:
  388. X *    Nothing.
  389. X */
  390. XSTATIC void
  391. Xdbprog_structupd(curstat_t *s)
  392. X{
  393. X    /* Disc title */
  394. X    if (cur_db.dtitle != NULL) {
  395. X        XmTextSetString(widgets.dbprog.dtitle_txt, cur_db.dtitle);
  396. X        XmTextSetInsertionPosition(
  397. X            widgets.dbprog.dtitle_txt,
  398. X            strlen(cur_db.dtitle)
  399. X        );
  400. X    }
  401. X    else {
  402. X        XmTextSetString(widgets.dbprog.dtitle_txt, UNDEF_STR);
  403. X        XmTextSetInsertionPosition(widgets.dbprog.dtitle_txt, 2);
  404. X    }
  405. X
  406. X
  407. X    /* Disc extended info popup */
  408. X    if (cur_db.extd != NULL)
  409. X        XmTextSetString(widgets.dbextd.disc_txt, cur_db.extd);
  410. X    else
  411. X        XmTextSetString(widgets.dbextd.disc_txt, "");
  412. X
  413. X    /* Number of tracks */
  414. X    cur_db.ntrk = s->tot_trks;
  415. X
  416. X    /* Track title list */
  417. X    sel_pos = -1;
  418. X    dbprog_listupd(s);
  419. X
  420. X    /* Track extended info popup: This is loaded when the user
  421. X     * pops it up.
  422. X     */
  423. X    XmTextSetString(widgets.dbextt.trk_txt, "");
  424. X
  425. X    /* Program sequence */
  426. X    if (cur_db.playorder != NULL) {
  427. X        XmTextSetString(widgets.dbprog.pgmseq_txt, cur_db.playorder);
  428. X        XmTextSetInsertionPosition(
  429. X            widgets.dbprog.pgmseq_txt,
  430. X            strlen(cur_db.playorder)
  431. X        );
  432. X
  433. X        XtSetSensitive(widgets.dbprog.clrpgm_btn, True);
  434. X        XtSetSensitive(widgets.dbprog.playpgm_btn, True);
  435. X    }
  436. X    else {
  437. X        XmTextSetString(widgets.dbprog.pgmseq_txt, "");
  438. X
  439. X        XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
  440. X        XtSetSensitive(widgets.dbprog.playpgm_btn, False);
  441. X    }
  442. X
  443. X    dbprog_changed = FALSE;
  444. X    XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  445. X    XtSetSensitive(widgets.dbprog.savedb_btn, False);
  446. X    XtSetSensitive(widgets.dbprog.extd_btn, True);
  447. X    XtSetSensitive(widgets.dbprog.extt_btn, False);
  448. X
  449. X    /* Update display */
  450. X    dpy_dbmode(s);
  451. X
  452. X    if (cur_db.dbfile == NULL && dbdirs[0] != NULL && dbdirs[1] == NULL) {
  453. X        /* Only one possible database directory: Set
  454. X         * database file path.
  455. X         */
  456. X
  457. X        cur_db.dbfile = (char *) MEM_ALLOC(strlen(dbdirs[0]) + 10);
  458. X        if (cur_db.dbfile == NULL) {
  459. X            cd_fatal_popup(
  460. X                app_data.str_fatal,
  461. X                app_data.str_nomemory
  462. X            );
  463. X            return;
  464. X        }
  465. X        sprintf(cur_db.dbfile, "%s/%08x", dbdirs[0], cur_db.discid);
  466. X    }
  467. X}
  468. X
  469. X
  470. X/*
  471. X * dbprog_extdupd
  472. X *    Update the Extended disc info text field in the cur_db structure
  473. X *    to match the contents shown in the text widget.
  474. X *
  475. X * Args:
  476. X *    Nothing
  477. X *
  478. X * Return:
  479. X *    Nothing.
  480. X */
  481. XSTATIC void
  482. Xdbprog_extdupd(void)
  483. X{
  484. X    /* Update in-core database structure */
  485. X    if (cur_db.extd != NULL)
  486. X        MEM_FREE(cur_db.extd);
  487. X
  488. X    if ((cur_db.extd = XmTextGetString(widgets.dbextd.disc_txt)) == NULL)
  489. X        return;
  490. X
  491. X    if (cur_db.extd[0] == '\0') {
  492. X        MEM_FREE(cur_db.extd);
  493. X        cur_db.extd = NULL;
  494. X    }
  495. X
  496. X    if (dbprog_changed)
  497. X        XtSetSensitive(widgets.dbprog.savedb_btn, True);
  498. X}
  499. X
  500. X
  501. X/*
  502. X * dbprog_exttupd
  503. X *    Update the Extended track info text field in the cur_db structure
  504. X *    to match the contents shown in the text widget.
  505. X *
  506. X * Args:
  507. X *    Nothing
  508. X *
  509. X * Return:
  510. X *    Nothing.
  511. X */
  512. XSTATIC void
  513. Xdbprog_exttupd(void)
  514. X{
  515. X    if (extt_pos < 0)
  516. X        return;
  517. X
  518. X    /* Update in-core database structure */
  519. X    if (cur_db.extt[extt_pos] != NULL)
  520. X        MEM_FREE(cur_db.extt[extt_pos]);
  521. X
  522. X    if ((cur_db.extt[extt_pos] =
  523. X        XmTextGetString(widgets.dbextt.trk_txt)) == NULL) {
  524. X        extt_pos = -1;
  525. X        return;
  526. X    }
  527. X
  528. X    if (cur_db.extt[extt_pos][0] == '\0') {
  529. X        MEM_FREE(cur_db.extt[extt_pos]);
  530. X        cur_db.extt[extt_pos] = NULL;
  531. X    }
  532. X
  533. X    extt_pos = -1;
  534. X
  535. X    if (dbprog_changed)
  536. X        XtSetSensitive(widgets.dbprog.savedb_btn, True);
  537. X}
  538. X
  539. X
  540. X/*
  541. X * dbprog_curtrkupd
  542. X *    Update the track list display to show the current playing
  543. X *    track entry in bold font.
  544. X *
  545. X * Args:
  546. X *    s - Pointer to the curstat_t structure.
  547. X *
  548. X * Return:
  549. X *    Nothing.
  550. X */
  551. Xvoid
  552. Xdbprog_curtrkupd(curstat_t *s)
  553. X{
  554. X    int            n,
  555. X                pos,
  556. X                secs;
  557. X    char            *str;
  558. X    XmString        xs;
  559. X    static int        sav_pos = -1;
  560. X    static sword32_t    sav_trkno;
  561. X    static byte_t        sav_tot_min,
  562. X                sav_tot_sec,
  563. X                sav_trk_min,
  564. X                sav_trk_sec;
  565. X
  566. X    if (sav_pos >= 0) {
  567. X        n = strlen((cur_db.trklist[sav_pos] == NULL) ?
  568. X               UNDEF_STR : cur_db.trklist[sav_pos]) + TRKLIST_PFLEN;
  569. X
  570. X        if ((str = (char *) MEM_ALLOC(n)) == NULL) {
  571. X            cd_fatal_popup(
  572. X                app_data.str_fatal,
  573. X                app_data.str_nomemory
  574. X            );
  575. X            return;
  576. X        }
  577. X
  578. X        if (cur_db.trklist[sav_pos] != NULL) {
  579. X            sprintf(str, TRKLIST_FMT,
  580. X                sav_trkno,
  581. X                (timemode == TIME_TOTAL) ?
  582. X                    sav_tot_min : sav_trk_min,
  583. X                (timemode == TIME_TOTAL) ?
  584. X                    sav_tot_sec : sav_trk_sec,
  585. X                cur_db.trklist[sav_pos]);
  586. X        }
  587. X        else {
  588. X            sprintf(str, TRKLIST_FMT,
  589. X                sav_trkno,
  590. X                (timemode == TIME_TOTAL) ?
  591. X                    sav_tot_min : sav_trk_min,
  592. X                (timemode == TIME_TOTAL) ?
  593. X                    sav_tot_sec : sav_trk_sec,
  594. X                UNDEF_STR);
  595. X        }
  596. X
  597. X        /* Restore previous playing track to original font */
  598. X
  599. X        xs = XmStringCreate(str, CHSET1);
  600. X
  601. X        XmListReplaceItemsPos(widgets.dbprog.trk_list, &xs,
  602. X                      1, sav_pos + 1);
  603. X
  604. X        XmStringFree(xs);
  605. X        MEM_FREE(str);
  606. X
  607. X        if (sel_pos == sav_pos + 1)
  608. X            /* This item is previously selected */
  609. X            XmListSelectPos(widgets.dbprog.trk_list,
  610. X                    sel_pos, False);
  611. X    }
  612. X
  613. X    if (s->cur_trk <= 0 || s->mode == M_NODISC) {
  614. X        sav_pos = -1;
  615. X        return;
  616. X    }
  617. X
  618. X    sav_pos = pos = curtrk_pos(s);
  619. X
  620. X    secs = ((s->trkinfo[pos+1].min * 60 +
  621. X        s->trkinfo[pos+1].sec) - 
  622. X            (s->trkinfo[pos].min * 60 +
  623. X        s->trkinfo[pos].sec));
  624. X
  625. X    sav_trkno = s->trkinfo[pos].trkno;
  626. X
  627. X    sav_tot_min = s->trkinfo[pos].min;
  628. X    sav_tot_sec = s->trkinfo[pos].sec;
  629. X    sav_trk_min = (byte_t) (secs / 60);
  630. X    sav_trk_sec = (byte_t) (secs % 60);
  631. X
  632. X    n = strlen((cur_db.trklist[pos] == NULL) ?
  633. X           UNDEF_STR : cur_db.trklist[pos]) + TRKLIST_PFLEN;
  634. X
  635. X    if ((str = (char *) MEM_ALLOC(n)) == NULL) {
  636. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  637. X        return;
  638. X    }
  639. X
  640. X    if (cur_db.trklist[pos] != NULL) {
  641. X        sprintf(str, TRKLIST_FMT,
  642. X            sav_trkno,
  643. X            (timemode == TIME_TOTAL) ? sav_tot_min : sav_trk_min,
  644. X            (timemode == TIME_TOTAL) ? sav_tot_sec : sav_trk_sec,
  645. X            cur_db.trklist[pos]);
  646. X    }
  647. X    else {
  648. X        sprintf(str, TRKLIST_FMT,
  649. X            sav_trkno,
  650. X            (timemode == TIME_TOTAL) ? sav_tot_min : sav_trk_min,
  651. X            (timemode == TIME_TOTAL) ? sav_tot_sec : sav_trk_sec,
  652. X            UNDEF_STR);
  653. X    }
  654. X
  655. X    /* Change current playing track to new font */
  656. X
  657. X    xs = XmStringCreate(str, CHSET2);
  658. X
  659. X    XmListReplaceItemsPos(widgets.dbprog.trk_list, &xs, 1, pos + 1);
  660. X
  661. X    XmStringFree(xs);
  662. X    MEM_FREE(str);
  663. X
  664. X    if (sel_pos == pos + 1)
  665. X        /* This item is previously selected */
  666. X        XmListSelectPos(widgets.dbprog.trk_list, sel_pos, False);
  667. X}
  668. X
  669. X
  670. X/*
  671. X * dbprog_dbclear
  672. X *    Clear in-core CD database entry.
  673. X *
  674. X * Args:
  675. X *    s - Pointer to the curstat_t structure.
  676. X *
  677. X * Return:
  678. X *    Nothing.
  679. X */
  680. Xvoid
  681. Xdbprog_dbclear(curstat_t *s)
  682. X{
  683. X    int        i;
  684. X
  685. X
  686. X    /* Pop down the extd and extt popups if necessary */
  687. X    if (XtIsManaged(widgets.dbextd.form))
  688. X        dbprog_extd_cancel(
  689. X            widgets.dbextd.cancel_btn,
  690. X            (XtPointer) s,
  691. X            (XtPointer) NULL
  692. X        );
  693. X    if (XtIsManaged(widgets.dbextt.form))
  694. X        dbprog_extt_cancel(
  695. X            widgets.dbextt.cancel_btn,
  696. X            (XtPointer) s,
  697. X            (XtPointer) NULL
  698. X        );
  699. X
  700. X
  701. X    if (cur_db.discid == 0)
  702. X        /* Already cleared */
  703. X        return;
  704. X
  705. X    /* Clear database entry structure */
  706. X
  707. X    if (cur_db.dbfile != NULL) {
  708. X        MEM_FREE(cur_db.dbfile);
  709. X        cur_db.dbfile = NULL;
  710. X    }
  711. X
  712. X    if (cur_db.dtitle != NULL) {
  713. X        MEM_FREE(cur_db.dtitle);
  714. X        cur_db.dtitle = NULL;
  715. X    }
  716. X
  717. X    if (cur_db.extd != NULL) {
  718. X        MEM_FREE(cur_db.extd);
  719. X        cur_db.extd = NULL;
  720. X    }
  721. X
  722. X    for (i = MAXTRACK-1; i >= 0; i--) {
  723. X        if (cur_db.trklist[i] != NULL) {
  724. X            MEM_FREE(cur_db.trklist[i]);
  725. X            cur_db.trklist[i] = NULL;
  726. X        }
  727. X
  728. X        if (cur_db.extt[i] != NULL) {
  729. X            MEM_FREE(cur_db.extt[i]);
  730. X            cur_db.extt[i] = NULL;
  731. X        }
  732. X
  733. X        if (cur_db.sav_extt[i] != NULL) {
  734. X            MEM_FREE(cur_db.sav_extt[i]);
  735. X            cur_db.sav_extt[i] = NULL;
  736. X        }
  737. X    }
  738. X
  739. X    if (cur_db.playorder != NULL) {
  740. X        MEM_FREE(cur_db.playorder);
  741. X        cur_db.playorder = NULL;
  742. X    }
  743. X
  744. X    cur_db.ntrk = 0;
  745. X    cur_db.discid = 0;
  746. X
  747. X    /* Update database/program display */
  748. X
  749. X    XmTextSetString(widgets.dbprog.dtitle_txt, "");
  750. X    XmListDeleteAllItems(widgets.dbprog.trk_list);
  751. X    XmTextSetString(widgets.dbprog.ttitle_txt, "");
  752. X    XmTextSetString(widgets.dbprog.pgmseq_txt, "");
  753. X    XmTextSetString(widgets.dbextd.disc_txt, "");
  754. X    XmTextSetString(widgets.dbextt.trk_txt, "");
  755. X
  756. X    /* Clear database flag */
  757. X
  758. X    s->cddb = FALSE;
  759. X    dbprog_changed = FALSE;
  760. X    osel_pos = -1;
  761. X
  762. X    /* Make some buttons insensitive */
  763. X    XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  764. X    XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
  765. X    XtSetSensitive(widgets.dbprog.playpgm_btn, False);
  766. X    XtSetSensitive(widgets.dbprog.savedb_btn, False);
  767. X    XtSetSensitive(widgets.dbprog.linkdb_btn, True);
  768. X    XtSetSensitive(widgets.dbprog.extd_btn, False);
  769. X    XtSetSensitive(widgets.dbprog.extt_btn, False);
  770. X}
  771. X
  772. X
  773. X/*
  774. X * dbprog_dbget
  775. X *    Read in the CD database file entry pertaining to the
  776. X *    currently loaded disc, if available.
  777. X *
  778. X * Args:
  779. X *    s - Pointer to the curstat_t structure.
  780. X *
  781. X * Return:
  782. X *    Nothing.
  783. X */
  784. Xvoid
  785. Xdbprog_dbget(curstat_t *s)
  786. X{
  787. X    int        i,
  788. X            pos,
  789. X            lineno;
  790. X    word32_t    discid;
  791. X    FILE        *fp = NULL;
  792. X    char        dbfile[FILE_PATH_SZ],
  793. X            buf[STR_BUF_SZ + 16],
  794. X            tmpbuf[STR_BUF_SZ + 16];
  795. X
  796. X    /* Get magic disc identifier */
  797. X    if ((discid = dbprog_discid(s)) == 0)
  798. X        /* Invalid identifier */
  799. X        return;
  800. X
  801. X    if (cur_db.discid == discid)
  802. X        /* Database entry already loaded: just return. */
  803. X        return;
  804. X
  805. X    cur_db.discid = discid;
  806. X
  807. X    /* Loop through all the database directories
  808. X     * and try to open the matching file for reading.
  809. X     */
  810. X    for (fp = NULL, i = 0; i < app_data.max_dbdirs; i++) {
  811. X        if (dbdirs[i] == NULL)
  812. X            break;
  813. X
  814. X        sprintf(dbfile, "%s/%08x", dbdirs[i], discid);
  815. X
  816. X        if ((fp = fopen(dbfile, "r")) != NULL)
  817. X            break;
  818. X    }
  819. X
  820. X    if (fp == NULL) {
  821. X        /* File does not exist or not readable */
  822. X
  823. X        /* Update list widget */
  824. X        dbprog_structupd(s);
  825. X
  826. X        return;
  827. X    }
  828. X
  829. X    /* Record the path to the database file. */
  830. X    cur_db.dbfile = (char *) MEM_ALLOC(strlen(dbfile) + 1);
  831. X    if (cur_db.dbfile == NULL) {
  832. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  833. X        return;
  834. X    }
  835. X    strcpy(cur_db.dbfile, dbfile);
  836. X
  837. X    /* Read first line of database file */
  838. X    if (fgets(buf, sizeof(buf), fp) == NULL) {
  839. X        fclose(fp);
  840. X        return;
  841. X    }
  842. X
  843. X    /* Database file signature check */
  844. X    if (strncmp(buf, "# xmcd ", 7) != 0) {
  845. X        /* Not a supported database file */
  846. X        fclose(fp);
  847. X        return;
  848. X    }
  849. X
  850. X    /* Read the rest of the database file */
  851. X    for (lineno = 0; fgets(buf, sizeof(buf), fp) != NULL; lineno++) {
  852. X        /* Comment line */
  853. X        if (buf[0] == '#') {
  854. X            lineno--;
  855. X            continue;
  856. X        }
  857. X
  858. X        buf[strlen(buf)-1] = '\n';
  859. X
  860. X        /* Disk ID sanity check */
  861. X        if (lineno == 0) {
  862. X            if (strncmp(buf, "DISCID=", 7) == 0)
  863. X                /* Okay */
  864. X                continue;
  865. X
  866. X            /* Sanity check failed */
  867. X            fclose(fp);
  868. X            return;
  869. X        }
  870. X
  871. X        /* Disk title */
  872. X        if (sscanf(buf, "DTITLE=%[^\n]\n", tmpbuf) > 0) {
  873. X            if (cur_db.dtitle == NULL) {
  874. X                cur_db.dtitle = (char *)
  875. X                    MEM_ALLOC(strlen(tmpbuf) + 1);
  876. X
  877. X                if (cur_db.dtitle != NULL)
  878. X                    cur_db.dtitle[0] = '\0';
  879. X            }
  880. X            else {
  881. X                cur_db.dtitle = (char *)
  882. X                    MEM_REALLOC(cur_db.dtitle,
  883. X                        strlen(cur_db.dtitle) +
  884. X                        strlen(tmpbuf) + 1);
  885. X            }
  886. X
  887. X            if (cur_db.dtitle == NULL) {
  888. X                cd_fatal_popup(
  889. X                    app_data.str_fatal,
  890. X                    app_data.str_nomemory
  891. X                );
  892. X                break;
  893. X            }
  894. X
  895. X            dbprog_strcat(cur_db.dtitle, tmpbuf);
  896. X            continue;
  897. X        }
  898. X
  899. X        /* Track title */
  900. X        if (sscanf(buf, "TTITLE%u=%[^\n]\n", &pos, tmpbuf) >= 2) {
  901. X            if (pos >= (int) s->tot_trks)
  902. X                continue;
  903. X
  904. X            if (cur_db.trklist[pos] == NULL) {
  905. X                cur_db.trklist[pos] = (char *)
  906. X                    MEM_ALLOC(strlen(tmpbuf) + 1);
  907. X
  908. X                if (cur_db.trklist[pos] != NULL)
  909. X                    cur_db.trklist[pos][0] = '\0';
  910. X                
  911. X            }
  912. X            else {
  913. X                cur_db.trklist[pos] = (char *)
  914. X                    MEM_REALLOC(cur_db.trklist[pos],
  915. X                        strlen(cur_db.trklist[pos]) +
  916. X                        strlen(tmpbuf) + 1);
  917. X            }
  918. X
  919. X            if (cur_db.trklist[pos] == NULL) {
  920. X                cd_fatal_popup(
  921. X                    app_data.str_fatal,
  922. X                    app_data.str_nomemory
  923. X                );
  924. X                break;
  925. X            }
  926. X
  927. X            dbprog_strcat(cur_db.trklist[pos], tmpbuf);
  928. X            continue;
  929. X        }
  930. X
  931. X        /* Disk extended info */
  932. X        if (sscanf(buf, "EXTD=%[^\n]\n", tmpbuf) > 0) {
  933. X            if (cur_db.extd == NULL) {
  934. X                cur_db.extd = (char *)
  935. X                    MEM_ALLOC(strlen(tmpbuf) + 1);
  936. X
  937. X                if (cur_db.extd != NULL)
  938. X                    cur_db.extd[0] = '\0';
  939. X            }
  940. X            else {
  941. X                cur_db.extd = (char *)
  942. X                    MEM_REALLOC(cur_db.extd,
  943. X                        strlen(cur_db.extd) +
  944. X                        strlen(tmpbuf) + 1);
  945. X            }
  946. X
  947. X            if (cur_db.extd == NULL) {
  948. X                cd_fatal_popup(
  949. X                    app_data.str_fatal,
  950. X                    app_data.str_nomemory
  951. X                );
  952. X                break;
  953. X            }
  954. X
  955. X            dbprog_strcat(cur_db.extd, tmpbuf);
  956. X            continue;
  957. X        }
  958. X
  959. X        /* Track extended info */
  960. X        if (sscanf(buf, "EXTT%u=%[^\n]\n", &pos, tmpbuf) >= 2) {
  961. X            if (pos >= (int) s->tot_trks)
  962. X                continue;
  963. X
  964. X            if (cur_db.extt[pos] == NULL) {
  965. X                cur_db.extt[pos] = (char *)
  966. X                    MEM_ALLOC(strlen(tmpbuf) + 1);
  967. X
  968. X                if (cur_db.extt[pos] != NULL)
  969. X                    cur_db.extt[pos][0] = '\0';
  970. X            }
  971. X            else {
  972. X                cur_db.extt[pos] = (char *)
  973. X                    MEM_REALLOC(cur_db.extt[pos],
  974. X                        strlen(cur_db.extt[pos]) +
  975. X                        strlen(tmpbuf) + 1);
  976. X            }
  977. X
  978. X            if (cur_db.extt[pos] == NULL) {
  979. X                cd_fatal_popup(
  980. X                    app_data.str_fatal,
  981. X                    app_data.str_nomemory
  982. X                );
  983. X                break;
  984. X            }
  985. X
  986. X            dbprog_strcat(cur_db.extt[pos], tmpbuf);
  987. X            continue;
  988. X        }
  989. X
  990. X        /* Play order */
  991. X        if (sscanf(buf, "PLAYORDER=%[^\n]\n", tmpbuf) > 0) {
  992. X            if (cur_db.playorder == NULL) {
  993. X                cur_db.playorder = (char *)
  994. X                    MEM_ALLOC(strlen(tmpbuf) + 1);
  995. X
  996. X                if (cur_db.playorder != NULL)
  997. X                    cur_db.playorder[0] = '\0';
  998. X                    
  999. X            }
  1000. X            else {
  1001. X                cur_db.playorder = (char *)
  1002. X                    MEM_REALLOC(cur_db.playorder,
  1003. X                        strlen(cur_db.playorder) +
  1004. X                        strlen(tmpbuf) + 1);
  1005. X            }
  1006. X
  1007. X            if (cur_db.playorder == NULL) {
  1008. X                cd_fatal_popup(
  1009. X                    app_data.str_fatal,
  1010. X                    app_data.str_nomemory
  1011. X                );
  1012. X                break;
  1013. X            }
  1014. X
  1015. X            dbprog_strcat(cur_db.playorder, tmpbuf);
  1016. X            continue;
  1017. X        }
  1018. X    }
  1019. X
  1020. X    fclose(fp);
  1021. X
  1022. X    s->cddb = TRUE;
  1023. X    XtSetSensitive(widgets.dbprog.savedb_btn, True);
  1024. X    XtSetSensitive(widgets.dbprog.linkdb_btn, False);
  1025. X
  1026. X    /* Update list widget */
  1027. X    dbprog_structupd(s);
  1028. X}
  1029. X
  1030. X
  1031. X/*
  1032. X * dbprog_putentry
  1033. X *    Write one information item into a database file.  Used by
  1034. X *    dbprog_dbput to update the CD database file.
  1035. X *
  1036. X * Args:
  1037. X *    fp - file stream handle
  1038. X *    idstr - The information identifier keyword text string
  1039. X *    entry - The information text string
  1040. X *
  1041. X * Return:
  1042. X *    Nothing.
  1043. X */
  1044. XSTATIC void
  1045. Xdbprog_putentry(FILE *fp, char *idstr, char *entry)
  1046. X{
  1047. X    int    i,
  1048. X        n;
  1049. X    char    *cp;
  1050. X
  1051. X    if (fp == NULL || idstr == NULL)
  1052. X        /* Paranoia */
  1053. X        return;
  1054. X
  1055. X    if (entry == NULL)
  1056. X        /* Null entry */
  1057. X        fprintf(fp, "%s=\n", idstr);
  1058. X    else {
  1059. X        /* Write entry to file, splitting into multiple lines
  1060. X         * if necessary.  Special handling for newline and tab
  1061. X         * characters.
  1062. X         */
  1063. X        cp = entry;
  1064. X
  1065. X        do {
  1066. X            fprintf(fp, "%s=", idstr);
  1067. X
  1068. X            n = MIN((int) strlen(cp), STR_BUF_SZ);
  1069. X
  1070. X            for (i = 0; i < n; i++, cp++) {
  1071. X                switch (*cp) {
  1072. X                case '\n':
  1073. X                    fprintf(fp, "\\n");
  1074. X                    break;
  1075. X                case '\t':
  1076. X                    fprintf(fp, "\\t");
  1077. X                    break;
  1078. X                default:
  1079. X                    fprintf(fp, "%c", *cp);
  1080. X                    break;
  1081. X                }
  1082. X            }
  1083. X
  1084. X            fprintf(fp, "\n");
  1085. X
  1086. X        } while (n == STR_BUF_SZ);
  1087. X    }
  1088. X}
  1089. X
  1090. X
  1091. X/*
  1092. X * dbprog_dbput
  1093. X *    Write in-core CD database entry to disc file.  This routine
  1094. X *    forks a child to do the actual file I/O.
  1095. X *
  1096. X * Args:
  1097. X *    s - Pointer to the curstat_t structure.
  1098. X *
  1099. X * Return:
  1100. X *    Nothing.
  1101. X */
  1102. XSTATIC void
  1103. Xdbprog_dbput(curstat_t *s)
  1104. X{
  1105. X    char        idstr[12],
  1106. X            errstr[STR_BUF_SZ];
  1107. X    int        i,
  1108. X            stat_val;
  1109. X    word32_t    trkaddr;
  1110. X    mode_t        dbfile_mode;
  1111. X    pid_t        cpid;
  1112. X    FILE        *fp;
  1113. X
  1114. X    if (cur_db.dbfile == NULL)
  1115. X        /* Output file undefined */
  1116. X        return;
  1117. X
  1118. X    /* Update structures if necessary */
  1119. X    if (XtIsManaged(widgets.dbextd.form))
  1120. X        dbprog_extdupd();
  1121. X    if (XtIsManaged(widgets.dbextt.form))
  1122. X        dbprog_exttupd();
  1123. X
  1124. X    switch (cpid = fork()) {
  1125. X    case 0:
  1126. X        break;
  1127. X    case -1:
  1128. X        sprintf(errstr, app_data.str_saverr_fork, errno);
  1129. X        cd_warning_popup(app_data.str_warning, errstr);
  1130. X        return;
  1131. X    default:
  1132. X        /* parent process: wait for child to exit */
  1133. X        while (waitpid(cpid, &stat_val, 0) != cpid)
  1134. X            ;
  1135. X
  1136. X        if (WIFEXITED(stat_val)) {
  1137. X            switch (WEXITSTATUS(stat_val)) {
  1138. X            case SETUID_ERR:
  1139. X                sprintf(errstr, app_data.str_saverr_suid, ouid);
  1140. X                cd_warning_popup(app_data.str_warning, errstr);
  1141. X                return;
  1142. X
  1143. X            case OPEN_ERR:
  1144. X                sprintf(errstr, app_data.str_saverr_open);
  1145. X                cd_warning_popup(app_data.str_warning, errstr);
  1146. X                return;
  1147. X
  1148. X            default:
  1149. X                break;
  1150. X            }
  1151. X        }
  1152. X        else if (WIFSIGNALED(stat_val)) {
  1153. X            sprintf(errstr, app_data.str_saverr_killed,
  1154. X                WTERMSIG(stat_val));
  1155. X            cd_warning_popup(app_data.str_warning, errstr);
  1156. X            return;
  1157. X        }
  1158. X
  1159. X        /* Database mode is on */
  1160. X        s->cddb = TRUE;
  1161. X
  1162. X        /* All edits have been saved, so clear flag */
  1163. X        dbprog_changed = FALSE;
  1164. X
  1165. X        /* Update display */
  1166. X        dpy_dbmode(s);
  1167. X
  1168. X        XtSetSensitive(widgets.dbprog.linkdb_btn, False);
  1169. X        return;
  1170. X    }
  1171. X
  1172. X    if (getuid() == 0 && setuid(ouid) < 0)
  1173. X        exit(SETUID_ERR);
  1174. X
  1175. X    if ((fp = fopen(cur_db.dbfile, "w")) == NULL)
  1176. X        exit(OPEN_ERR);
  1177. X
  1178. X    /* File header */
  1179. X    fprintf(fp, "# xmcd %s CD database file\n", VERSION);
  1180. X    fprintf(fp, "# Copyright (C) 1993 Ti Kan\n");
  1181. X
  1182. X    fprintf(fp, "#\n# Track frame offsets:\n");
  1183. X    for (i = 0; i < (int) cur_db.ntrk; i++) {
  1184. X        msftoblk(
  1185. X            s->trkinfo[i].min,
  1186. X            s->trkinfo[i].sec,
  1187. X            s->trkinfo[i].frame,
  1188. X            &trkaddr,
  1189. X            0
  1190. X        );
  1191. X        fprintf(fp, "#\t%u\n", trkaddr);
  1192. X    }
  1193. X
  1194. X    fprintf(fp, "#\n# Disc length: %u seconds\n#\n",
  1195. X        s->trkinfo[(int) cur_db.ntrk].min * 60 +
  1196. X        s->trkinfo[(int) cur_db.ntrk].sec);
  1197. X
  1198. X    /* Disc ID magic number */
  1199. X    fprintf(fp, "DISCID=%08x\n", cur_db.discid);
  1200. X
  1201. X    /* Disc artist/title */
  1202. X    dbprog_putentry(fp, "DTITLE", cur_db.dtitle);
  1203. X
  1204. X    /* Track titles */
  1205. X    for (i = 0; i < (int) cur_db.ntrk; i++) {
  1206. X        sprintf(idstr, "TTITLE%u", i);
  1207. X        dbprog_putentry(fp, idstr, cur_db.trklist[i]);
  1208. X    }
  1209. X
  1210. X    /* Extended disc information */
  1211. X    dbprog_putentry(fp, "EXTD", cur_db.extd);
  1212. X
  1213. X    /* Extended track information */
  1214. X    for (i = 0; i < (int) cur_db.ntrk; i++) {
  1215. X        sprintf(idstr, "EXTT%u", i);
  1216. X        dbprog_putentry(fp, idstr, cur_db.extt[i]);
  1217. X    }
  1218. X
  1219. X    /* Track program sequence */
  1220. X    dbprog_putentry(fp, "PLAYORDER", cur_db.playorder);
  1221. X
  1222. X    fclose(fp);
  1223. X
  1224. X    /* Set the database file permissions */
  1225. X    sscanf(app_data.dbfile_mode, "%o", &dbfile_mode);
  1226. X    chmod(cur_db.dbfile, dbfile_mode);
  1227. X
  1228. X    /* Child exits here. */
  1229. X    exit(0);
  1230. X}
  1231. X
  1232. X
  1233. X/*
  1234. X * dbprog_add_linkent
  1235. X *    Add an entry to the link-search list in sorted order.  Used
  1236. X *    by dbprog_bld_linkopts.
  1237. X *
  1238. X * Args:
  1239. X *    dtitle - Disc artist/title text string
  1240. X *    idstr - A text string representation of the magic number
  1241. X *
  1242. X * Return:
  1243. X *    Nothing.
  1244. X */
  1245. XSTATIC void
  1246. Xdbprog_add_linkent(char *dtitle, char *idstr)
  1247. X{
  1248. X    linkopts_t    *p,
  1249. X            *q,
  1250. X            *r;
  1251. X
  1252. X    if (dtitle == NULL || idstr == NULL)
  1253. X        /* Paranoia */
  1254. X        return;
  1255. X
  1256. X    if ((p = (linkopts_t *)(void *)
  1257. X         MEM_ALLOC(sizeof(linkopts_t))) == NULL) {
  1258. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  1259. X        return;
  1260. X    }
  1261. X    if ((p->dtitle = (char *) MEM_ALLOC(strlen(dtitle) + 1)) == NULL) {
  1262. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  1263. X        return;
  1264. X    }
  1265. X
  1266. X    strcpy(p->idstr, idstr);
  1267. X    strcpy(p->dtitle, dtitle);
  1268. X
  1269. X    if (linkhead == NULL) {
  1270. X        /* This is the first element */
  1271. X        linkhead = p;
  1272. X        p->next = NULL;
  1273. X    }
  1274. X    else {
  1275. X        /* Add to list in sorted order */
  1276. X        for (q = linkhead, r = NULL; q != NULL; q = q->next) {
  1277. X            if (strcmp(dtitle, q->dtitle) < 0)
  1278. X                break;
  1279. X            r = q;
  1280. X        }
  1281. X        if (r == NULL) {
  1282. X            p->next = linkhead;
  1283. X            linkhead = p;
  1284. X        }
  1285. X        else {
  1286. X            p->next = r->next;
  1287. X            r->next = p;
  1288. X        }
  1289. X    }
  1290. X}
  1291. X
  1292. X
  1293. X/*
  1294. X * dbprog_bld_linkopts
  1295. X *    Build a sorted linked list of track titles which are to be
  1296. X *    used to present to the user for database search-link.
  1297. X *
  1298. X * Args:
  1299. X *    Norhing.
  1300. X *
  1301. X * Return:
  1302. X *    Nothing.
  1303. X */
  1304. XSTATIC linkopts_t *
  1305. Xdbprog_bld_linkopts(void)
  1306. X{
  1307. X    int        n;
  1308. X    char        *dbdir,
  1309. X            *bname,
  1310. X            tmppath[FILE_PATH_SZ],
  1311. X            buf[STR_BUF_SZ + 16];
  1312. X    FILE        *fp;
  1313. X    DIR        *dp;
  1314. X    struct dirent    *de;
  1315. X
  1316. X    /* Warning: This code is SYSV-ish.  Porting to other
  1317. X     * environment may require some modification here.
  1318. X     */
  1319. X
  1320. X    if (cur_db.dbfile == NULL)
  1321. X        /* Error */
  1322. X        return(NULL);
  1323. X
  1324. X    dbdir = dirname(cur_db.dbfile);
  1325. X    bname = basename(cur_db.dbfile);
  1326. X
  1327. X    if ((dp = opendir(dbdir)) == NULL)
  1328. X        return(NULL);
  1329. X
  1330. X    while ((de = readdir(dp)) != NULL) {
  1331. X        /* Find all entries in this directory with the same
  1332. X         * number of tracks as this disc.
  1333. X         */
  1334. X        if (strncmp(de->d_name + 6, bname + 6, 2) != 0)
  1335. X            continue;
  1336. X
  1337. X        sprintf(tmppath, "%s/%s", dbdir, de->d_name);
  1338. X        if ((fp = fopen(tmppath, "r")) == NULL)
  1339. X            continue;
  1340. X        
  1341. X        /* Read first line of database file */
  1342. X        if (fgets(buf, sizeof(buf), fp) == NULL) {
  1343. X            fclose(fp);
  1344. X            continue;
  1345. X        }
  1346. X
  1347. X        /* Database file signature check */
  1348. X        if (strncmp(buf, "# xmcd ", 7) != 0) {
  1349. X            /* Not a supported database file */
  1350. X            fclose(fp);
  1351. X            continue;
  1352. X        }
  1353. X
  1354. X        while (fgets(buf, sizeof(buf), fp) != NULL) {
  1355. X            /* Look for disk title */
  1356. X            if (strncmp(buf, "DTITLE=", 7) == 0) {
  1357. X                /* Eat newline */
  1358. X                n = strlen(buf) - 1;
  1359. X                if (buf[n] == '\n')
  1360. X                    buf[n] = '\0';
  1361. X                
  1362. X                /* Add to list in sorted order */
  1363. X                dbprog_add_linkent(buf + 7, de->d_name);
  1364. X
  1365. X                break;
  1366. X            }
  1367. X        }
  1368. X
  1369. X        fclose(fp);
  1370. X    }
  1371. X    closedir(dp);
  1372. X
  1373. X    return(linkhead);
  1374. X}
  1375. X
  1376. X
  1377. X/*
  1378. X * dbprog_free_linkopts
  1379. X *    Dismantle the sorted linked list of track titles created by
  1380. X *    dbprog_bld_linkopts.
  1381. X *
  1382. X * Args:
  1383. X *    Nothing.
  1384. X *
  1385. X * Return:
  1386. X *    Nothing.
  1387. X */
  1388. XSTATIC void
  1389. Xdbprog_free_linkopts(void)
  1390. X{
  1391. X    linkopts_t    *p,
  1392. X            *q;
  1393. X
  1394. X    for (p = q = linkhead; p != NULL; p = q) {
  1395. X        q = p->next;
  1396. X        if (p->dtitle != NULL)
  1397. X            MEM_FREE(p->dtitle);
  1398. X        MEM_FREE((char *) p);
  1399. X    }
  1400. X    linkhead = NULL;
  1401. X}
  1402. X
  1403. X
  1404. X/*
  1405. X * dbprog_dblink
  1406. X *    Pop up the search-link popup window, to let the user pick
  1407. X *    an existing CD database file entry to link the current disc to.
  1408. X *
  1409. X * Args:
  1410. X *    s - Pointer to the curstat_t structure.
  1411. X *
  1412. X * Return:
  1413. X *    Nothing.
  1414. X */
  1415. X/*ARGSUSED*/
  1416. XSTATIC void
  1417. Xdbprog_dblink(curstat_t *s)
  1418. X{
  1419. X    int        i;
  1420. X    XmString    xs;
  1421. X    linkopts_t    *p;
  1422. X
  1423. X    if (cur_db.dbfile == NULL)
  1424. X        /* Output file undefined */
  1425. X        return;
  1426. X
  1427. X    /* Search directory for possible alternatives, and allow
  1428. X     * user to select a file to link to.
  1429. X     */
  1430. X
  1431. X    XmListDeleteAllItems(widgets.linksel.link_list);
  1432. X
  1433. X    linkhead = dbprog_bld_linkopts();
  1434. X
  1435. X    for (i = 0, p = linkhead; p != NULL; i++, p = p->next) {
  1436. X        xs = XmStringCreateSimple(p->dtitle);
  1437. X        XmListAddItemUnselected(widgets.linksel.link_list, xs, i + 1);
  1438. X        XmStringFree(xs);
  1439. X    }
  1440. X
  1441. X    if (i == 0) {
  1442. X        cd_info_popup(app_data.str_info, app_data.str_nolink);
  1443. X    }
  1444. X    else if (!XtIsManaged(widgets.linksel.form)) {
  1445. X        linksel_pos = 0;
  1446. X
  1447. X        /* Pop up the link selector window */
  1448. X        XtManageChild(widgets.linksel.form);
  1449. X    }
  1450. X}
  1451. X
  1452. X
  1453. X/*
  1454. X * dbprog_init
  1455. X *    Initialize the CD database/program subsystem.
  1456. X *
  1457. X * Args:
  1458. X *    s - Pointer to the curstat_t structure.
  1459. X *
  1460. X * Return:
  1461. X *    Nothing.
  1462. X */
  1463. Xvoid
  1464. Xdbprog_init(curstat_t *s)
  1465. X{
  1466. X    /* Clear the in-core structure */
  1467. X    dbprog_dbclear(s);
  1468. X}
  1469. X
  1470. X
  1471. X/**************** vv Callback routines vv ****************/
  1472. X
  1473. X/*
  1474. X * dbprog_popup
  1475. X *    Pop up the database/program subsystem window.
  1476. X */
  1477. Xvoid
  1478. Xdbprog_popup(Widget w, XtPointer client_data, XtPointer call_data)
  1479. X{
  1480. X    if (!XtIsManaged(widgets.dbprog.form)) {
  1481. X        /* Pop up the dbprog window */
  1482. X        XtManageChild(widgets.dbprog.form);
  1483. X
  1484. X        /* Pop up the extd/extt windows if necessary */
  1485. X        if (extd_manage)
  1486. X            dbprog_extd(w, client_data, call_data);
  1487. X
  1488. X        if (extt_manage)
  1489. X            dbprog_extt(w, (XtPointer) FALSE, call_data);
  1490. X    }
  1491. X}
  1492. X
  1493. X
  1494. X/*
  1495. X * dbprog_dtitle_new
  1496. X *    Disc title editor text widget callback function.
  1497. X */
  1498. X/*ARGSUSED*/
  1499. Xvoid
  1500. Xdbprog_dtitle_new(Widget w, XtPointer client_data, XtPointer call_data)
  1501. X{
  1502. X    XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  1503. X    char            *str;
  1504. X    XmString        xs;
  1505. X
  1506. X    if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_VALUE_CHANGED)
  1507. X        return;
  1508. X
  1509. X    if ((str = XmTextGetString(w)) == NULL)
  1510. X        return;
  1511. X
  1512. X    if (strcmp(str, UNDEF_STR) == 0) {
  1513. X        if (cur_db.dtitle != NULL) {
  1514. X            MEM_FREE(cur_db.dtitle);
  1515. X            cur_db.dtitle = NULL;
  1516. X        }
  1517. X        XtFree(str);
  1518. X        return;
  1519. X    }
  1520. X
  1521. X    dbprog_changed = TRUE;
  1522. X    XtSetSensitive(widgets.dbprog.savedb_btn, True);
  1523. X
  1524. X    if (cur_db.dtitle != NULL)
  1525. X        MEM_FREE(cur_db.dtitle);
  1526. X
  1527. X    if (str[0] == '\0') {
  1528. X        XtFree(str);
  1529. X        cur_db.dtitle = NULL;
  1530. X    }
  1531. X    else
  1532. X        cur_db.dtitle = str;
  1533. X
  1534. X    /* Update the extd window if necessary */
  1535. X    if (XtIsManaged(widgets.dbextd.form)) {
  1536. X        if (cur_db.dtitle == NULL)
  1537. X            xs = XmStringCreateSimple("Untitled");
  1538. X        else
  1539. X            xs = XmStringCreateSimple(cur_db.dtitle);
  1540. X
  1541. X        XtVaSetValues(widgets.dbextd.disc_lbl,
  1542. X            XmNlabelString, xs,
  1543. X            NULL
  1544. X        );
  1545. X
  1546. X        XmStringFree(xs);
  1547. X    }
  1548. X}
  1549. X
  1550. X
  1551. X/*
  1552. X * dbprog_trklist_play
  1553. X *    Track list entry selection default action callback.
  1554. X */
  1555. Xvoid
  1556. Xdbprog_trklist_play(Widget w, XtPointer client_data, XtPointer call_data)
  1557. X{
  1558. X    XmListCallbackStruct    *p = (XmListCallbackStruct *)(void *) call_data;
  1559. X    curstat_t        *s = (curstat_t *)(void *) client_data;
  1560. X
  1561. X    if (p->reason != XmCR_DEFAULT_ACTION)
  1562. X        return;
  1563. X
  1564. X    sel_pos = osel_pos = p->item_position;
  1565. X
  1566. X    dbprog_clrpgm(widgets.dbprog.clrpgm_btn, (XtPointer) s, (XtPointer) p);
  1567. X
  1568. X    /* Set this again because dbprog_clrprog() changes it */
  1569. X    sel_pos = p->item_position;
  1570. X
  1571. X    dbprog_addpgm(widgets.dbprog.addpgm_btn, (XtPointer) s, (XtPointer) p);
  1572. X    dbprog_playpgm(
  1573. X        widgets.dbprog.playpgm_btn,
  1574. X        (XtPointer) s,
  1575. X        (XtPointer) p
  1576. X    );
  1577. X
  1578. X    sel_pos = -1;
  1579. X    XmListDeselectAllItems(w);
  1580. X    XmTextSetString(widgets.dbprog.ttitle_txt, "");
  1581. X
  1582. X    XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  1583. X    XtSetSensitive(widgets.dbprog.extt_btn, False);
  1584. X}
  1585. X
  1586. X
  1587. X/*
  1588. X * dbprog_trklist_select
  1589. X *    Track list entry selection callback.
  1590. X */
  1591. Xvoid
  1592. Xdbprog_trklist_select(Widget w, XtPointer client_data, XtPointer call_data)
  1593. X{
  1594. X    XmListCallbackStruct    *p = (XmListCallbackStruct *)(void *) call_data;
  1595. X    curstat_t        *s = (curstat_t *)(void *) client_data;
  1596. X    int            n,
  1597. X                secs;
  1598. X    char            *cp,
  1599. X                *str;
  1600. X    byte_t            min,
  1601. X                sec;
  1602. X    XmString        xs;
  1603. X
  1604. X    if (p->reason != XmCR_BROWSE_SELECT)
  1605. X        return;
  1606. X
  1607. X    if (!cdlib_check_disc(s) || s->mode == M_NODISC)
  1608. X        return;
  1609. X
  1610. X    if (title_edited) {
  1611. X        title_edited = FALSE;
  1612. X        osel_pos = p->item_position;
  1613. X
  1614. X        if ((cp = XmTextGetString(widgets.dbprog.ttitle_txt)) == NULL)
  1615. X            return;
  1616. X
  1617. X        if (timemode == TIME_TOTAL) {
  1618. X            min = s->trkinfo[p->item_position-1].min;
  1619. X            sec = s->trkinfo[p->item_position-1].sec;
  1620. X        }
  1621. X        else {
  1622. X            secs = ((s->trkinfo[p->item_position].min * 60 +
  1623. X                s->trkinfo[p->item_position].sec) - 
  1624. X                (s->trkinfo[p->item_position-1].min * 60 +
  1625. X                s->trkinfo[p->item_position-1].sec));
  1626. X            min = (byte_t) (secs / 60);
  1627. X            sec = (byte_t) (secs % 60);
  1628. X        }
  1629. X
  1630. X        n = strlen(cp) + TRKLIST_PFLEN;
  1631. X        if ((str = (char *) MEM_ALLOC(n)) == NULL) {
  1632. X            cd_fatal_popup(
  1633. X                app_data.str_fatal,
  1634. X                app_data.str_nomemory
  1635. X            );
  1636. X            return;
  1637. X        }
  1638. X
  1639. X        sprintf(str, TRKLIST_FMT,
  1640. X            s->trkinfo[p->item_position-1].trkno,
  1641. X            min, sec, cp);
  1642. X
  1643. X        if (s->mode != M_NODISC && s->cur_trk >= 0 &&
  1644. X            curtrk_pos(s) == p->item_position-1)
  1645. X            xs = XmStringCreate(str, CHSET2);
  1646. X        else
  1647. X            xs = XmStringCreate(str, CHSET1);
  1648. X
  1649. X        XmListReplaceItemsPos(w, &xs, 1, p->item_position);
  1650. X
  1651. X        XmStringFree(xs);
  1652. X        MEM_FREE(str);
  1653. X
  1654. X        sel_pos = -1;
  1655. X        XmListDeselectAllItems(widgets.dbprog.trk_list);
  1656. X        XmTextSetString(widgets.dbprog.ttitle_txt, "");
  1657. X
  1658. X        if (cur_db.trklist[p->item_position-1] != NULL)
  1659. X            MEM_FREE(cur_db.trklist[p->item_position-1]);
  1660. X
  1661. X        cur_db.trklist[p->item_position-1] = cp;
  1662. X
  1663. X        dbprog_changed = TRUE;
  1664. X        XtSetSensitive(widgets.dbprog.savedb_btn, True);
  1665. X        XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  1666. X        XtSetSensitive(widgets.dbprog.extt_btn, False);
  1667. X
  1668. X        /* Update the extt window if necessary */
  1669. X        if (extt_pos == p->item_position-1 &&
  1670. X            XtIsManaged(widgets.dbextt.form)) {
  1671. X            if (cur_db.trklist[p->item_position-1] == NULL)
  1672. X                xs = XmStringCreateSimple("Untitled");
  1673. X            else
  1674. X                xs = XmStringCreateSimple(
  1675. X                    cur_db.trklist[p->item_position-1]
  1676. X                );
  1677. X
  1678. X            XtVaSetValues(widgets.dbextt.trk_lbl,
  1679. X                XmNlabelString, xs,
  1680. X                NULL
  1681. X            );
  1682. X
  1683. X            XmStringFree(xs);
  1684. X        }
  1685. X
  1686. X        /* Return the input focus to the track title editor */
  1687. X        XmProcessTraversal(
  1688. X            widgets.dbprog.ttitle_txt,
  1689. X            XmTRAVERSE_CURRENT
  1690. X        );
  1691. X    }
  1692. X    else if (sel_pos == p->item_position) {
  1693. X        /* This item is already selected: deselect it */
  1694. X
  1695. X        sel_pos = -1;
  1696. X        XmListDeselectAllItems(w);
  1697. X        XmTextSetString(widgets.dbprog.ttitle_txt, "");
  1698. X
  1699. X        XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  1700. X        XtSetSensitive(widgets.dbprog.extt_btn, False);
  1701. X    }
  1702. X    else {
  1703. X        sel_pos = osel_pos = p->item_position;
  1704. X
  1705. X        if (cur_db.trklist[p->item_position-1] == NULL) {
  1706. X            XmTextSetString(widgets.dbprog.ttitle_txt, UNDEF_STR);
  1707. X            XmTextSetInsertionPosition(
  1708. X                widgets.dbprog.ttitle_txt,
  1709. X                strlen(UNDEF_STR)
  1710. X            );
  1711. X        }
  1712. X        else {
  1713. X            XmTextSetString(widgets.dbprog.ttitle_txt,
  1714. X                    cur_db.trklist[p->item_position-1]);
  1715. X            XmTextSetInsertionPosition(
  1716. X                widgets.dbprog.ttitle_txt,
  1717. X                strlen(cur_db.trklist[p->item_position-1])
  1718. X            );
  1719. X        }
  1720. X
  1721. X        XtSetSensitive(widgets.dbprog.addpgm_btn, True);
  1722. X        XtSetSensitive(widgets.dbprog.extt_btn, True);
  1723. X
  1724. X        /* Warp the extt window to the new selected track, if
  1725. X         * it is popped up.
  1726. X         */
  1727. X        if (XtIsManaged(widgets.dbextt.form))
  1728. X            dbprog_extt(w, (XtPointer) FALSE, call_data);
  1729. X    }
  1730. X}
  1731. X
  1732. X
  1733. X/*
  1734. X * dbprog_ttitle_new
  1735. X *    Track title editor text widget callback function.
  1736. X */
  1737. Xvoid
  1738. Xdbprog_ttitle_new(Widget w, XtPointer client_data, XtPointer call_data)
  1739. X{
  1740. X    XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  1741. X    curstat_t        *s = (curstat_t *)(void *) client_data;
  1742. X    char            *cp,
  1743. X                *str;
  1744. X    int            *pos,
  1745. X                secs,
  1746. X                i,
  1747. X                n;
  1748. X    byte_t            min,
  1749. X                sec;
  1750. X    XmString        xs;
  1751. X    XmListCallbackStruct    cb;
  1752. X
  1753. X    if (p->reason == XmCR_VALUE_CHANGED) {
  1754. X        if ((cp = XmTextGetString(w)) == NULL)
  1755. X            return;
  1756. X
  1757. X        if (cp[0] == '\0')
  1758. X            title_edited = FALSE;
  1759. X        else if (sel_pos < 0)
  1760. X            title_edited = TRUE;
  1761. X
  1762. X        XtFree(cp);
  1763. X        return;
  1764. X    }
  1765. X    else if (p->reason != XmCR_ACTIVATE)
  1766. X        return;
  1767. X
  1768. X    if (sel_pos >= 0 &&
  1769. X        XmListGetSelectedPos(widgets.dbprog.trk_list, &pos, &i)) {
  1770. X        if ((cp = XmTextGetString(w)) == NULL)
  1771. X            return;
  1772. X
  1773. X        if (pos == NULL) {
  1774. X            XtFree(cp);
  1775. X            return;
  1776. X        }
  1777. X
  1778. X        if (i == 1) {
  1779. X            if (timemode == TIME_TOTAL) {
  1780. X                min = s->trkinfo[(*pos)-1].min;
  1781. X                sec = s->trkinfo[(*pos)-1].sec;
  1782. X            }
  1783. X            else {
  1784. X                secs = ((s->trkinfo[*pos].min * 60 +
  1785. X                    s->trkinfo[*pos].sec) - 
  1786. X                    (s->trkinfo[(*pos)-1].min * 60 +
  1787. X                    s->trkinfo[(*pos)-1].sec));
  1788. X                min = (byte_t) (secs / 60);
  1789. X                sec = (byte_t) (secs % 60);
  1790. X            }
  1791. X
  1792. X            n = strlen(cp) + TRKLIST_PFLEN;
  1793. X            if ((str = (char *) MEM_ALLOC(n)) == NULL) {
  1794. X                cd_fatal_popup(
  1795. X                    app_data.str_fatal,
  1796. X                    app_data.str_nomemory
  1797. X                );
  1798. X                return;
  1799. X            }
  1800. X
  1801. X            sprintf(str, TRKLIST_FMT,
  1802. X                s->trkinfo[(*pos)-1].trkno,
  1803. X                min, sec, cp);
  1804. X
  1805. X            if (s->mode != M_NODISC && s->cur_trk >= 0 &&
  1806. X                curtrk_pos(s) == ((*pos)-1))
  1807. X                xs = XmStringCreate(str, CHSET2);
  1808. X            else
  1809. X                xs = XmStringCreate(str, CHSET1);
  1810. X
  1811. X            XmListReplaceItemsPos(widgets.dbprog.trk_list,
  1812. X                          &xs, 1, *pos);
  1813. X            XmStringFree(xs);
  1814. X            MEM_FREE(str);
  1815. X
  1816. X            sel_pos = -1;
  1817. X            XmListDeselectAllItems(widgets.dbprog.trk_list);
  1818. X
  1819. X            if (cur_db.trklist[(*pos)-1] != NULL)
  1820. X                MEM_FREE(cur_db.trklist[(*pos)-1]);
  1821. X
  1822. X            cur_db.trklist[(*pos)-1] = cp;
  1823. X
  1824. X            dbprog_changed = TRUE;
  1825. X            XtSetSensitive(widgets.dbprog.savedb_btn, True);
  1826. X            XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  1827. X            XtSetSensitive(widgets.dbprog.extt_btn, False);
  1828. X
  1829. X            /* Update the extt window if necessary */
  1830. X            if (extt_pos == (*pos)-1 &&
  1831. X                XtIsManaged(widgets.dbextt.form)) {
  1832. X                if (cur_db.trklist[(*pos)-1] == NULL)
  1833. X                    xs = XmStringCreateSimple("Untitled");
  1834. X                else
  1835. X                    xs = XmStringCreateSimple(
  1836. X                        cur_db.trklist[(*pos)-1]
  1837. X                    );
  1838. X
  1839. X                XtVaSetValues(widgets.dbextt.trk_lbl,
  1840. X                    XmNlabelString, xs,
  1841. X                    NULL
  1842. X                );
  1843. X
  1844. X                XmStringFree(xs);
  1845. X            }
  1846. X        }
  1847. X
  1848. X        XmTextSetString(w, "");
  1849. X
  1850. X        XtFree((XtPointer) pos);
  1851. X    }
  1852. X    else {
  1853. X        /* Pressing Return in this case is equivalent to clicking
  1854. X         * on the next title-less track on the track list.
  1855. X         */
  1856. X        if (osel_pos <= 0)
  1857. X            i = 0;
  1858. X        else
  1859. X            i = osel_pos - 1;
  1860. X
  1861. X        for (; i < (int) s->tot_trks; i++) {
  1862. X            if (cur_db.trklist[i] == NULL) {
  1863. X                cb.item_position = i + 1;
  1864. X                cb.reason = XmCR_BROWSE_SELECT;
  1865. X                cb.event = p->event;
  1866. X
  1867. X                dbprog_trklist_select(
  1868. X                    widgets.dbprog.trk_list,
  1869. X                    (XtPointer) s,
  1870. X                    (XtPointer) &cb
  1871. X                );
  1872. X
  1873. X                break;
  1874. X            }
  1875. X        }
  1876. X
  1877. X    }
  1878. X}
  1879. X
  1880. X
  1881. X/*
  1882. X * dbprog_pgmseq_verify
  1883. X *    Play sequence editor text widget user-input verification callback.
  1884. X */
  1885. X/*ARGSUSED*/
  1886. Xvoid
  1887. Xdbprog_pgmseq_verify(Widget w, XtPointer client_data, XtPointer call_data)
  1888. X{
  1889. X    XmTextVerifyCallbackStruct
  1890. X        *p = (XmTextVerifyCallbackStruct *)(void *) call_data;
  1891. X    int    i;
  1892. X
  1893. X    if (p->reason != XmCR_MODIFYING_TEXT_VALUE)
  1894. X        return;
  1895. X
  1896. X    p->doit = True;
  1897. X
  1898. X    /* If not changing via direct edit in the text widget,
  1899. X     * then there is nothing to do here.
  1900. X     */
  1901. X    if (!pgmseq_editing)
  1902. X        return;
  1903. X
  1904. X    if (p->startPos != p->endPos)
  1905. X        return;
  1906. X
  1907. X    switch (p->text->format) {
  1908. X    case FMT8BIT:
  1909. X        if (p->text->length != 1) {
  1910. X            p->doit = False;
  1911. X            return;
  1912. X        }
  1913. X
  1914. X        if (p->text->ptr[0] == PGM_SEPCHAR) {
  1915. X            if (cur_db.playorder == NULL ||
  1916. X                (i = strlen(cur_db.playorder)) == 0 ||
  1917. X                cur_db.playorder[i-1] == PGM_SEPCHAR ||
  1918. X                p->currInsert != p->newInsert)
  1919. X                p->doit = False;
  1920. X        }
  1921. X        else if (!isdigit(p->text->ptr[0]))
  1922. X            p->doit = False;
  1923. X        break;
  1924. X
  1925. X    case FMT16BIT:
  1926. X        /* Don't know how to handle 16-bit character sets yet */
  1927. X        p->doit = False;
  1928. X        return;
  1929. X    }
  1930. X}
  1931. X
  1932. X
  1933. X/*
  1934. X * dbprog_pgmseq_txtchg
  1935. X *    Play sequence editor text widget text changed callback.
  1936. X */
  1937. X/*ARGSUSED*/
  1938. Xvoid
  1939. Xdbprog_pgmseq_txtchg(Widget w, XtPointer client_data, XtPointer call_data)
  1940. X{
  1941. X    XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  1942. X
  1943. X    if (p->reason != XmCR_VALUE_CHANGED)
  1944. X        return;
  1945. X
  1946. X    /* If not changing via direct edit in the text widget,
  1947. X     * then there is nothing to do here.
  1948. X     */
  1949. X    if (!pgmseq_editing)
  1950. X        return;
  1951. X
  1952. X    if (cur_db.playorder != NULL)
  1953. X        MEM_FREE(cur_db.playorder);
  1954. X
  1955. X    cur_db.playorder = XmTextGetString(w);
  1956. X
  1957. X    dbprog_changed = TRUE;
  1958. X    XtSetSensitive(widgets.dbprog.savedb_btn, True);
  1959. X    XtSetSensitive(widgets.dbprog.playpgm_btn, True);
  1960. X    XtSetSensitive(widgets.dbprog.clrpgm_btn, True);
  1961. X}
  1962. X
  1963. X
  1964. X/*
  1965. X * dbprog_pgmseq_focuschg
  1966. X *    Play sequence editor text widget keyboard focus change callback.
  1967. X */
  1968. X/*ARGSUSED*/
  1969. Xvoid
  1970. Xdbprog_pgmseq_focuschg(Widget w, XtPointer client_data, XtPointer call_data)
  1971. X{
  1972. X    XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  1973. X
  1974. X    if (p->reason == XmCR_FOCUS)
  1975. X        pgmseq_editing = TRUE;
  1976. X    else if (p->reason == XmCR_LOSING_FOCUS)
  1977. X        pgmseq_editing = FALSE;
  1978. X}
  1979. X
  1980. X
  1981. X/*
  1982. X * dbprog_addpgm
  1983. X *    Program Add button callback.
  1984. X */
  1985. X/*ARGSUSED*/
  1986. Xvoid
  1987. Xdbprog_addpgm(Widget w, XtPointer client_data, XtPointer call_data)
  1988. X{
  1989. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  1990. X    char        *cp,
  1991. X            tmpbuf[6];
  1992. X
  1993. X    if (sel_pos < 0 || !cdlib_check_disc(s) || s->mode == M_NODISC) {
  1994. X        cd_beep();
  1995. X        return;
  1996. X    }
  1997. X
  1998. X    if (cur_db.playorder == NULL) {
  1999. X        sprintf(tmpbuf, "%u", s->trkinfo[sel_pos-1].trkno);
  2000. X        cp = (char *) MEM_ALLOC(strlen(tmpbuf) + 1);
  2001. X    }
  2002. X    else {
  2003. X        sprintf(tmpbuf, "%c%u",
  2004. X            PGM_SEPCHAR, s->trkinfo[sel_pos-1].trkno);
  2005. X        cp = (char *) MEM_ALLOC(
  2006. X            strlen(cur_db.playorder) + strlen(tmpbuf) + 1
  2007. X        );
  2008. X    }
  2009. X
  2010. X    if (cp == NULL) {
  2011. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  2012. X        return;
  2013. X    }
  2014. X
  2015. X    sprintf(cp, "%s%s",
  2016. X        (cur_db.playorder == NULL) ? "" : cur_db.playorder,
  2017. X        tmpbuf);
  2018. X
  2019. X    MEM_FREE(cur_db.playorder);
  2020. X    cur_db.playorder = cp;
  2021. X
  2022. X    XmTextSetString(widgets.dbprog.pgmseq_txt, cur_db.playorder);
  2023. X    XmTextSetInsertionPosition(
  2024. X        widgets.dbprog.pgmseq_txt,
  2025. X        strlen(cur_db.playorder)
  2026. X    );
  2027. X
  2028. X    dbprog_changed = TRUE;
  2029. X    XtSetSensitive(widgets.dbprog.savedb_btn, True);
  2030. X    XtSetSensitive(widgets.dbprog.playpgm_btn, True);
  2031. X    XtSetSensitive(widgets.dbprog.clrpgm_btn, True);
  2032. X}
  2033. X
  2034. X
  2035. X/*
  2036. X * dbprog_clrpgm
  2037. X *    Program Clear button callback.
  2038. X */
  2039. X/*ARGSUSED*/
  2040. Xvoid
  2041. Xdbprog_clrpgm(Widget w, XtPointer client_data, XtPointer call_data)
  2042. X{
  2043. X    if (cur_db.playorder != NULL) {
  2044. X        MEM_FREE(cur_db.playorder);
  2045. X        cur_db.playorder = NULL;
  2046. X
  2047. X        XmTextSetString(widgets.dbprog.pgmseq_txt, "");
  2048. X    }
  2049. X
  2050. X    sel_pos = -1;
  2051. X    XmListDeselectAllItems(widgets.dbprog.trk_list);
  2052. X    XmTextSetString(widgets.dbprog.ttitle_txt, "");
  2053. X
  2054. X    dbprog_changed = TRUE;
  2055. X    XtSetSensitive(widgets.dbprog.savedb_btn, True);
  2056. X    XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  2057. X    XtSetSensitive(widgets.dbprog.playpgm_btn, False);
  2058. X    XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
  2059. X    XtSetSensitive(widgets.dbprog.extt_btn, False);
  2060. X}
  2061. X
  2062. X
  2063. X/*
  2064. X * dbprog_playpgm
  2065. X *    Program Play button callback.
  2066. X */
  2067. X/*ARGSUSED*/
  2068. Xvoid
  2069. Xdbprog_playpgm(Widget w, XtPointer client_data, XtPointer call_data)
  2070. X{
  2071. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2072. X
  2073. X    if (!cdlib_check_disc(s) || s->mode == M_NODISC) {
  2074. X        cd_beep();
  2075. X        return;
  2076. X    }
  2077. X
  2078. X    sel_pos = -1;
  2079. X    XmListDeselectAllItems(widgets.dbprog.trk_list);
  2080. X    XmTextSetString(widgets.dbprog.ttitle_txt, "");
  2081. X
  2082. X    XtSetSensitive(widgets.dbprog.addpgm_btn, False);
  2083. X    XtSetSensitive(widgets.dbprog.extt_btn, False);
  2084. X
  2085. X    /* Parse play order string and set the play order */
  2086. X    if (s->mode != M_NODISC && !dbprog_parse(s)) {
  2087. X        cd_warning_popup(app_data.str_warning, app_data.str_seqfmterr);
  2088. X        return;
  2089. X    }
  2090. X
  2091. X    if (s->prog_tot == 0)
  2092. X        /* No program to run: just return */
  2093. X        return;
  2094. X
  2095. X    if (s->shuffle) {
  2096. X        /* Cancel shuffle mode */
  2097. X        s->shuffle = FALSE;
  2098. X        set_shuffle_btn(False);
  2099. X    }
  2100. X
  2101. X    /* Play the program */
  2102. X    cd_playprog(s);
  2103. X}
  2104. X
  2105. X
  2106. X/*
  2107. X * dbprog_savedb
  2108. X *    In-core CD database SAVE button callback.
  2109. X */
  2110. X/*ARGSUSED*/
  2111. Xvoid
  2112. Xdbprog_savedb(Widget w, XtPointer client_data, XtPointer call_data)
  2113. X{
  2114. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2115. X    static bool_t    first = TRUE;
  2116. X
  2117. X    if (cur_db.discid == 0) {
  2118. X        cd_beep();
  2119. X        return;
  2120. X    }
  2121. X
  2122. X    if (dbdirs[0] == NULL) {
  2123. X        /* No database directory */
  2124. X        cd_info_popup(app_data.str_info, app_data.str_nodb);
  2125. X        return;
  2126. X    }
  2127. X
  2128. X    dirsel_mode = DIRSEL_SAVE;
  2129. X
  2130. X    if (cur_db.dbfile == NULL) {
  2131. X        /* Pop up the database directory selection popup */
  2132. X        sel_pos = -1;
  2133. X        XmListDeselectAllItems(widgets.dirsel.dir_list);
  2134. X        XmTextSetString(widgets.dbprog.ttitle_txt, "");
  2135. X
  2136. X        XtManageChild(widgets.dirsel.form);
  2137. X
  2138. X        if (first) {
  2139. X            first = FALSE;
  2140. X            XmProcessTraversal(
  2141. X                widgets.dirsel.ok_btn,
  2142. X                XmTRAVERSE_CURRENT
  2143. X            );
  2144. X        }
  2145. X    }
  2146. X    else
  2147. X        /* Save to file directly */
  2148. X        dbprog_dbput(s);
  2149. X}
  2150. X
  2151. X
  2152. X/*
  2153. X * dbprog_loaddb
  2154. X *    CD database file LOAD button callback.
  2155. X */
  2156. X/*ARGSUSED*/
  2157. Xvoid
  2158. Xdbprog_loaddb(Widget w, XtPointer client_data, XtPointer call_data)
  2159. X{
  2160. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2161. X
  2162. X    if (!cdlib_check_disc(s) || s->mode == M_NODISC) {
  2163. X        cd_beep();
  2164. X        return;
  2165. X    }
  2166. X
  2167. X    /* Clear the in-core entry */
  2168. X    dbprog_dbclear(s);
  2169. X
  2170. X    /* Re-load from database file */
  2171. X    dbprog_dbget(s);
  2172. X}
  2173. X
  2174. X
  2175. X/*
  2176. X * dbprog_link
  2177. X *    CD Database file search-link button callback.
  2178. X */
  2179. X/*ARGSUSED*/
  2180. Xvoid
  2181. Xdbprog_link(Widget w, XtPointer client_data, XtPointer call_data)
  2182. X{
  2183. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2184. X    static bool_t    first = TRUE;
  2185. X
  2186. X    if (cur_db.discid == 0) {
  2187. X        cd_beep();
  2188. X        return;
  2189. X    }
  2190. X
  2191. X    dirsel_mode = DIRSEL_LINK;
  2192. X
  2193. X    if (cur_db.dbfile != NULL) {
  2194. X        MEM_FREE(cur_db.dbfile);
  2195. X        cur_db.dbfile = NULL;
  2196. X    }
  2197. X
  2198. X    if (dbdirs[0] == NULL) {
  2199. X        /* No database directory */
  2200. X        cd_info_popup(app_data.str_info, app_data.str_nodb);
  2201. X        return;
  2202. X    }
  2203. X
  2204. X    if (dbdirs[1] == NULL) {
  2205. X        /* Only one possible database directory: Set
  2206. X         * database file path.
  2207. X         */
  2208. X
  2209. X        cur_db.dbfile = (char *) MEM_ALLOC(strlen(dbdirs[0]) + 10);
  2210. X        if (cur_db.dbfile == NULL) {
  2211. X            cd_fatal_popup(
  2212. X                app_data.str_fatal,
  2213. X                app_data.str_nomemory
  2214. X            );
  2215. X            return;
  2216. X        }
  2217. X        sprintf(cur_db.dbfile, "%s/%08x", dbdirs[0], cur_db.discid);
  2218. X
  2219. X        dbprog_dblink(s);
  2220. X        return;
  2221. X    }
  2222. X
  2223. X    /* Pop up the database directory selection popup */
  2224. X    sel_pos = -1;
  2225. X    XmListDeselectAllItems(widgets.dirsel.dir_list);
  2226. X    XmTextSetString(widgets.dbprog.ttitle_txt, "");
  2227. X
  2228. X    if (first) {
  2229. X        first = FALSE;
  2230. X        XmProcessTraversal(
  2231. X            widgets.dirsel.ok_btn,
  2232. X            XmTRAVERSE_CURRENT
  2233. X        );
  2234. X    }
  2235. X
  2236. X    XtManageChild(widgets.dirsel.form);
  2237. X}
  2238. X
  2239. X
  2240. X/*
  2241. X * dbprog_cancel
  2242. X *    Pop down CD database/program window.
  2243. X */
  2244. X/*ARGSUSED*/
  2245. Xvoid
  2246. Xdbprog_cancel(Widget w, XtPointer client_data, XtPointer call_data)
  2247. X{
  2248. X    XmPushButtonCallbackStruct
  2249. X            *p = (XmPushButtonCallbackStruct *)(void *) call_data;
  2250. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2251. X
  2252. X    if (XtIsManaged(widgets.dbextd.form)) {
  2253. X        /* Force a popdown of the extd window */
  2254. X        dbprog_extd_cancel(
  2255. X            widgets.dbextd.cancel_btn,
  2256. X            (XtPointer) s,
  2257. X            (XtPointer) p
  2258. X        );
  2259. X
  2260. X        extd_manage = TRUE;
  2261. X    }
  2262. X    if (XtIsManaged(widgets.dbextt.form)) {
  2263. X        /* Force a popdown of the extt window */
  2264. X        dbprog_extt_cancel(
  2265. X            widgets.dbextt.cancel_btn,
  2266. X            (XtPointer) s,
  2267. X            (XtPointer) p
  2268. X        );
  2269. X
  2270. X        extt_manage = TRUE;
  2271. X    }
  2272. X
  2273. X    /* Pop down the database/program dialog */
  2274. X    XtUnmanageChild(widgets.dbprog.form);
  2275. X}
  2276. X
  2277. X
  2278. X/*
  2279. X * dbprog_timedpy
  2280. X *    Toggle the time display mode in the track list.
  2281. X */
  2282. X/*ARGSUSED*/
  2283. Xvoid
  2284. Xdbprog_timedpy(Widget w, XtPointer client_data, XtPointer call_data)
  2285. X{
  2286. X    XmRowColumnCallbackStruct
  2287. X            *p = (XmRowColumnCallbackStruct *)(void *) call_data;
  2288. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2289. X    static bool_t    btn0set = TRUE,
  2290. X            btn1set = FALSE;
  2291. X    static int    seq = 0;
  2292. X
  2293. X    if (p->widget == widgets.dbprog.tottime_btn)
  2294. X        btn0set = !btn0set;
  2295. X    else if (p->widget == widgets.dbprog.trktime_btn)
  2296. X        btn1set = !btn1set;
  2297. X    else
  2298. X        return;    /* Error: invalid widget */
  2299. X
  2300. X    switch (seq) {
  2301. X    case 0:
  2302. X        seq++;
  2303. X        break;
  2304. X    case 1:
  2305. X        if (btn0set && !btn1set)
  2306. X            timemode = TIME_TOTAL;
  2307. X        else if (!btn0set && btn1set)
  2308. X            timemode = TIME_TRACK;
  2309. X
  2310. X        if (cdlib_check_disc(s) && s->mode != M_NODISC) {
  2311. X            XmListDeleteAllItems(widgets.dbprog.trk_list);
  2312. X            dbprog_listupd(s);
  2313. X        }
  2314. X
  2315. X        seq = 0;
  2316. X        break;
  2317. X    }
  2318. X}
  2319. X
  2320. X
  2321. X/*
  2322. X * dbprog_extd
  2323. X *    Pop up/down the disc extended info window.
  2324. X */
  2325. Xvoid
  2326. Xdbprog_extd(Widget w, XtPointer client_data, XtPointer call_data)
  2327. X{
  2328. X    XmString    xs;
  2329. X    static bool_t    first = TRUE;
  2330. X
  2331. X    if (XtIsManaged(widgets.dbextd.form)) {
  2332. X        /* Pop down the Disc Extended Info window */
  2333. X        dbprog_extd_ok(w, client_data, call_data);
  2334. X        return;
  2335. X    }
  2336. X
  2337. X    if (cur_db.dtitle == NULL)
  2338. X        xs = XmStringCreateSimple("Untitled");
  2339. X    else
  2340. X        xs = XmStringCreateSimple(cur_db.dtitle);
  2341. X
  2342. X    XtVaSetValues(widgets.dbextd.disc_lbl,
  2343. X        XmNlabelString, xs,
  2344. X        NULL
  2345. X    );
  2346. X
  2347. X    XmStringFree(xs);
  2348. X
  2349. X    /* Pop up the Disc Extended Info window */
  2350. X    XtManageChild(widgets.dbextd.form);
  2351. X
  2352. X    if (first) {
  2353. X        first = FALSE;
  2354. X        XmProcessTraversal(
  2355. X            widgets.dbextd.ok_btn,
  2356. X            XmTRAVERSE_CURRENT
  2357. X        );
  2358. X    }
  2359. X
  2360. X    extd_manage = FALSE;
  2361. X}
  2362. X
  2363. X
  2364. X/*
  2365. X * dbprog_extt
  2366. X *    Pop up/down the track extended info window.
  2367. X */
  2368. Xvoid
  2369. Xdbprog_extt(Widget w, XtPointer client_data, XtPointer call_data)
  2370. X{
  2371. X    int        i,
  2372. X            n,
  2373. X            *pos;
  2374. X    XmString    xs;
  2375. X    bool_t        from_main = (bool_t)(int) client_data;
  2376. X    static bool_t    first = TRUE;
  2377. X
  2378. X    if (XtIsManaged(widgets.dbextt.form)) {
  2379. X        if (from_main) {
  2380. X            /* Pop down the Track Extended Info window */
  2381. X            dbprog_extt_ok(w, client_data, call_data);
  2382. X
  2383. X            return;
  2384. X        }
  2385. X        else {
  2386. X            /* Update structures */
  2387. X            dbprog_exttupd();
  2388. X        }
  2389. X    }
  2390. X
  2391. X    if (sel_pos >= 0 &&
  2392. X        XmListGetSelectedPos(widgets.dbprog.trk_list, &pos, &n)) {
  2393. X
  2394. X        /* Enter extt setup mode */
  2395. X        extt_setup = TRUE;
  2396. X
  2397. X        if (n != 1) {
  2398. X            /* This shouldn't happen error */
  2399. X            cd_beep();
  2400. X            return;
  2401. X        }
  2402. X
  2403. X        if (cur_db.trklist[(*pos)-1] == NULL)
  2404. X            xs = XmStringCreateSimple("Untitled");
  2405. X        else
  2406. X            xs = XmStringCreateSimple(cur_db.trklist[(*pos)-1]);
  2407. X
  2408. X        XtVaSetValues(widgets.dbextt.trk_lbl,
  2409. X            XmNlabelString, xs,
  2410. X            NULL
  2411. X        );
  2412. X
  2413. X        XmStringFree(xs);
  2414. X
  2415. X        /* Track extended info text */
  2416. X        if (cur_db.extt[(*pos)-1] != NULL)
  2417. X            XmTextSetString(widgets.dbextt.trk_txt,
  2418. X                        cur_db.extt[(*pos)-1]);
  2419. X        else
  2420. X            XmTextSetString(widgets.dbextt.trk_txt, "");
  2421. X
  2422. X        extt_pos = (*pos)-1;
  2423. X
  2424. X        if (from_main) {
  2425. X            /* Save a backup copy of the text in case the user
  2426. X             * wants to abort.  This code will ensure that the
  2427. X             * data is not saved twice while the extended track
  2428. X             * info window is popped up.
  2429. X             */
  2430. X            for (i = 0; i < MAXTRACK; i++) {
  2431. X                if (cur_db.sav_extt[i] == NULL &&
  2432. X                    cur_db.extt[i] != NULL) {
  2433. X                    cur_db.sav_extt[i] = (char *)
  2434. X                        MEM_ALLOC(
  2435. X                            strlen(cur_db.extt[i]) + 1
  2436. X                        );
  2437. X                    if (cur_db.sav_extt[i] == NULL) {
  2438. X                        cd_fatal_popup(
  2439. X                            app_data.str_fatal,
  2440. X                            app_data.str_nomemory
  2441. X                        );
  2442. X                        return;
  2443. X                    }
  2444. X                    strcpy(
  2445. X                        cur_db.sav_extt[i],
  2446. X                        cur_db.extt[i]
  2447. X                    );
  2448. X                }
  2449. X            }
  2450. X        }
  2451. X
  2452. X        /* Pop up the Track Extended Info popup */
  2453. X        XtManageChild(widgets.dbextt.form);
  2454. X
  2455. X        if (first) {
  2456. X            first = FALSE;
  2457. X            XmProcessTraversal(
  2458. X                widgets.dbextt.ok_btn,
  2459. X                XmTRAVERSE_CURRENT
  2460. X            );
  2461. X        }
  2462. X
  2463. X        extt_manage = FALSE;
  2464. X
  2465. X        /* Exit extt setup mode */
  2466. X        extt_setup = FALSE;
  2467. X    }
  2468. X}
  2469. X
  2470. X
  2471. X/*
  2472. X * dbprog_set_changed
  2473. X *    Set the flag indicating that the user has made changes to the
  2474. X *    in-core CD database entry.
  2475. X */
  2476. X/*ARGSUSED*/
  2477. Xvoid
  2478. Xdbprog_set_changed(Widget w, XtPointer client_data, XtPointer call_data)
  2479. X{
  2480. X    XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  2481. X
  2482. X    if (p->reason != XmCR_VALUE_CHANGED)
  2483. X        return;
  2484. X
  2485. X    /* Setup of the extt window is not a user change */
  2486. X    if (!extt_setup) {
  2487. X        dbprog_changed = TRUE;
  2488. X
  2489. X        if (!XtIsSensitive(widgets.dbprog.savedb_btn))
  2490. X            XtSetSensitive(widgets.dbprog.savedb_btn, True);
  2491. X    }
  2492. X}
  2493. X
  2494. X
  2495. X/*
  2496. X * dbprog_extd_ok
  2497. X *    Extended disc info window OK button callback.
  2498. X */
  2499. X/*ARGSUSED*/
  2500. Xvoid
  2501. Xdbprog_extd_ok(Widget w, XtPointer client_data, XtPointer call_data)
  2502. X{
  2503. X    /* Pop down the Disc Extended Info popup */
  2504. X    XtUnmanageChild(widgets.dbextd.form);
  2505. X
  2506. X    /* Update structures */
  2507. X    dbprog_extdupd();
  2508. X}
  2509. X
  2510. X
  2511. X/*
  2512. X * dbprog_extd_clear
  2513. X *    Extended disc info window Clear button callback.
  2514. X */
  2515. X/*ARGSUSED*/
  2516. Xvoid
  2517. Xdbprog_extd_clear(Widget w, XtPointer client_data, XtPointer call_data)
  2518. X{
  2519. X    XmTextSetString(widgets.dbextd.disc_txt, "");
  2520. X}
  2521. X
  2522. X
  2523. X/*
  2524. X * dbprog_extd_cancel
  2525. X *    Extended disc info window Cancel button callback.
  2526. X */
  2527. X/*ARGSUSED*/
  2528. Xvoid
  2529. Xdbprog_extd_cancel(Widget w, XtPointer client_data, XtPointer call_data)
  2530. X{
  2531. X    /* Pop down the Disc Extended Info popup */
  2532. X    XtUnmanageChild(widgets.dbextd.form);
  2533. X
  2534. X    /* Restore original text */
  2535. X    if (cur_db.extd == NULL)
  2536. X        XmTextSetString(widgets.dbextd.disc_txt, "");
  2537. X    else
  2538. X        XmTextSetString(widgets.dbextd.disc_txt, cur_db.extd);
  2539. X}
  2540. X
  2541. X
  2542. X/*
  2543. X * dbprog_extt_ok
  2544. X *    Extended track info window OK button callback.
  2545. X */
  2546. X/*ARGSUSED*/
  2547. Xvoid
  2548. Xdbprog_extt_ok(Widget w, XtPointer client_data, XtPointer call_data)
  2549. X{
  2550. X    int    i;
  2551. X
  2552. X    /* Pop down the Track Extended Info popup */
  2553. X    XtUnmanageChild(widgets.dbextt.form);
  2554. X
  2555. X    /* Update structures */
  2556. X    dbprog_exttupd();
  2557. X
  2558. X    /* Delete backup text */
  2559. X    for (i = 0; i < MAXTRACK; i++) {
  2560. X        if (cur_db.sav_extt[i] != NULL) {
  2561. X            MEM_FREE(cur_db.sav_extt[i]);
  2562. X            cur_db.sav_extt[i] = NULL;
  2563. X        }
  2564. X    }
  2565. X}
  2566. X
  2567. X
  2568. X/*
  2569. X * dbprog_extt_clear
  2570. X *    Extended track info window Clear button callback.
  2571. X */
  2572. X/*ARGSUSED*/
  2573. Xvoid
  2574. Xdbprog_extt_clear(Widget w, XtPointer client_data, XtPointer call_data)
  2575. X{
  2576. X    XmTextSetString(widgets.dbextt.trk_txt, "");
  2577. X}
  2578. X
  2579. X
  2580. X/*
  2581. X * dbprog_extt_cancel
  2582. X *    Extended track info window Cancel button callback.
  2583. X */
  2584. X/*ARGSUSED*/
  2585. Xvoid
  2586. Xdbprog_extt_cancel(Widget w, XtPointer client_data, XtPointer call_data)
  2587. X{
  2588. X    int    i;
  2589. X
  2590. X    /* Pop down the Track Extended Info popup */
  2591. X    XtUnmanageChild(widgets.dbextt.form);
  2592. X
  2593. X    /* Restore backup text */
  2594. X    for (i = 0; i < MAXTRACK; i++) {
  2595. X        if (cur_db.extt[i] != NULL)
  2596. X            MEM_FREE(cur_db.extt[i]);
  2597. X
  2598. X        cur_db.extt[i] = cur_db.sav_extt[i];
  2599. X        cur_db.sav_extt[i] = NULL;
  2600. X    }
  2601. X}
  2602. X
  2603. X
  2604. X/*
  2605. X * dbprog_dirsel_select
  2606. X *    CD Database directory selection list callback.
  2607. X */
  2608. X/*ARGSUSED*/
  2609. Xvoid
  2610. Xdbprog_dirsel_select(Widget w, XtPointer client_data, XtPointer call_data)
  2611. X{
  2612. X    XmListCallbackStruct    *p = (XmListCallbackStruct *)(void *) call_data;
  2613. X
  2614. X    if (p->reason != XmCR_BROWSE_SELECT)
  2615. X        return;
  2616. X
  2617. X    if (cur_db.dbfile != NULL)
  2618. X        MEM_FREE(cur_db.dbfile);
  2619. X
  2620. X    cur_db.dbfile = (char *)
  2621. X        MEM_ALLOC(strlen(dbdirs[p->item_position-1]) + 10);
  2622. X
  2623. X    if (cur_db.dbfile == NULL) {
  2624. X        cd_fatal_popup(app_data.str_fatal, app_data.str_nomemory);
  2625. X        return;
  2626. X    }
  2627. X
  2628. X    sprintf(cur_db.dbfile, "%s/%08x",
  2629. X        dbdirs[p->item_position-1], cur_db.discid);
  2630. X}
  2631. X
  2632. X
  2633. X/*
  2634. X * dbprog_dirsel_ok
  2635. X *    CD Database directory selection window OK button callback.
  2636. X */
  2637. X/*ARGSUSED*/
  2638. Xvoid
  2639. Xdbprog_dirsel_ok(Widget w, XtPointer client_data, XtPointer call_data)
  2640. X{
  2641. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2642. X
  2643. X    if (cur_db.dbfile == NULL) {
  2644. X        /* User has not selected a directory yet */
  2645. X        cd_beep();
  2646. X        return;
  2647. X    }
  2648. X
  2649. X    /* Pop down the database directory selector popup dialog */
  2650. X    XtUnmanageChild(widgets.dirsel.form);
  2651. X
  2652. X    switch (dirsel_mode) {
  2653. X    case DIRSEL_SAVE:
  2654. X        /* Save the database entry to output file */
  2655. X        dbprog_dbput(s);
  2656. X        break;
  2657. X
  2658. X    case DIRSEL_LINK:
  2659. X        /* Link the database entry to another file */
  2660. X        dbprog_dblink(s);
  2661. X        break;
  2662. X
  2663. X    default:
  2664. X        /* Shouldn't get here */
  2665. X        break;
  2666. X    }
  2667. X}
  2668. X
  2669. X
  2670. X/*
  2671. X * dbprog_dirsel_cancel
  2672. X *    CD Database directory selection window Cancel button callback.
  2673. X */
  2674. X/*ARGSUSED*/
  2675. Xvoid
  2676. Xdbprog_dirsel_cancel(Widget w, XtPointer client_data, XtPointer call_data)
  2677. X{
  2678. X    /* Pop down the database directory selector popup dialog */
  2679. X    XtUnmanageChild(widgets.dirsel.form);
  2680. X
  2681. X    /* Clear database file path */
  2682. X    if (cur_db.dbfile != NULL) {
  2683. X        MEM_FREE(cur_db.dbfile);
  2684. X        cur_db.dbfile = NULL;
  2685. X    }
  2686. X}
  2687. X
  2688. X
  2689. X/*
  2690. X * dbprog_linksel_select
  2691. X *    Search-link selector list user-selection callback.
  2692. X */
  2693. X/*ARGSUSED*/
  2694. Xvoid
  2695. Xdbprog_linksel_select(Widget w, XtPointer client_data, XtPointer call_data)
  2696. X{
  2697. X    XmListCallbackStruct    *p = (XmListCallbackStruct *)(void *) call_data;
  2698. X
  2699. X    if (p->reason != XmCR_BROWSE_SELECT)
  2700. X        return;
  2701. X
  2702. X    linksel_pos = p->item_position;
  2703. X}
  2704. X
  2705. X
  2706. X/*
  2707. X * dbprog_linksel_ok
  2708. X *    Search-link selector window OK button callback.
  2709. X */
  2710. Xvoid
  2711. Xdbprog_linksel_ok(Widget w, XtPointer client_data, XtPointer call_data)
  2712. X{
  2713. X    curstat_t    *s = (curstat_t *)(void *) client_data;
  2714. X    char        errstr[STR_BUF_SZ],
  2715. X            ltarget[FILE_PATH_SZ];
  2716. X    int        i,
  2717. X            stat_val;
  2718. X    linkopts_t    *q;
  2719. X    pid_t        cpid;
  2720. X
  2721. X    if (linksel_pos <= 0) {
  2722. X        /* User has not selected a link target yet */
  2723. X        cd_beep();
  2724. X        return;
  2725. X    }
  2726. X
  2727. X    /* Pop down the link selector popup dialog */
  2728. X    XtUnmanageChild(widgets.linksel.form);
  2729. X
  2730. X    /* Do the link */
  2731. X    switch (cpid = fork()) {
  2732. X    case 0:
  2733. X        break;
  2734. X    case -1:
  2735. X        sprintf(errstr, app_data.str_saverr_fork, errno);
  2736. X        cd_warning_popup(app_data.str_warning, errstr);
  2737. X        return;
  2738. X    default:
  2739. X        /* parent process: wait for child to exit */
  2740. X        while (waitpid(cpid, &stat_val, 0) != cpid)
  2741. X            ;
  2742. X
  2743. X        /* Free link options list */
  2744. X        dbprog_free_linkopts();
  2745. X
  2746. X        if (WIFEXITED(stat_val)) {
  2747. X            switch (WEXITSTATUS(stat_val)) {
  2748. X            case SETUID_ERR:
  2749. X                sprintf(errstr, app_data.str_lnkerr_suid, ouid);
  2750. X                cd_warning_popup(app_data.str_warning, errstr);
  2751. X                return;
  2752. X
  2753. X            case LINK_ERR:
  2754. X                sprintf(errstr, app_data.str_lnkerr_link);
  2755. X                cd_warning_popup(app_data.str_warning, errstr);
  2756. X                return;
  2757. X
  2758. X            default:
  2759. X                break;
  2760. X            }
  2761. X        }
  2762. X        else if (WIFSIGNALED(stat_val)) {
  2763. X            sprintf(errstr, app_data.str_saverr_killed,
  2764. X                WTERMSIG(stat_val));
  2765. X            cd_warning_popup(app_data.str_warning, errstr);
  2766. X            return;
  2767. X        }
  2768. X
  2769. X        /* Load new database entry */
  2770. X        dbprog_loaddb(w, client_data, call_data);
  2771. X
  2772. X        /* Database mode is now on */
  2773. X        s->cddb = TRUE;
  2774. X
  2775. X        /* All edits have been saved, so clear flag */
  2776. X        dbprog_changed = FALSE;
  2777. X
  2778. X        /* Update display */
  2779. X        dpy_dbmode(s);
  2780. X
  2781. X        XtSetSensitive(widgets.dbprog.linkdb_btn, False);
  2782. X        return;
  2783. X    }
  2784. X
  2785. X    if (getuid() == 0 && setuid(ouid) < 0)
  2786. X        exit(SETUID_ERR);
  2787. X
  2788. X    for (i = 0, q = linkhead; q != NULL; i++, q = q->next)
  2789. X        if (i == linksel_pos - 1)
  2790. X            break;
  2791. X
  2792. X    if (q == NULL)
  2793. X        /* This should not happen */
  2794. X        exit(LINK_ERR);
  2795. X
  2796. X    sprintf(ltarget, "%s/%s", dirname(cur_db.dbfile), q->idstr);
  2797. X
  2798. X#ifdef USE_SYMLINK
  2799. X    if (symlink(ltarget, cur_db.dbfile) < 0)
  2800. X#else
  2801. X    if (link(ltarget, cur_db.dbfile) < 0)
  2802. X#endif
  2803. X        exit(LINK_ERR);
  2804. X
  2805. X    /* Child exits here. */
  2806. X    exit(0);
  2807. X}
  2808. X
  2809. X
  2810. X/*
  2811. X * dbprog_linksel_cancel
  2812. X *    Search-link selector window Cancel button callback.
  2813. X */
  2814. X/*ARGSUSED*/
  2815. Xvoid
  2816. Xdbprog_linksel_cancel(Widget w, XtPointer client_data, XtPointer call_data)
  2817. X{
  2818. X    /* Pop down the link selector popup dialog */
  2819. X    XtUnmanageChild(widgets.linksel.form);
  2820. X
  2821. X    /* Free link options list */
  2822. X    dbprog_free_linkopts();
  2823. X
  2824. X    /* Clear database file path */
  2825. X    if (cur_db.dbfile != NULL) {
  2826. X        MEM_FREE(cur_db.dbfile);
  2827. X        cur_db.dbfile = NULL;
  2828. X    }
  2829. X}
  2830. X
  2831. X/**************** ^^ Callback routines ^^ ****************/
  2832. X
  2833. END_OF_FILE
  2834. if test 58201 -ne `wc -c <'dbprog.c'`; then
  2835.     echo shar: \"'dbprog.c'\" unpacked with wrong size!
  2836. fi
  2837. # end of 'dbprog.c'
  2838. fi
  2839. echo shar: End of archive 6 \(of 13\).
  2840. cp /dev/null ark6isdone
  2841. MISSING=""
  2842. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
  2843.     if test ! -f ark${I}isdone ; then
  2844.     MISSING="${MISSING} ${I}"
  2845.     fi
  2846. done
  2847. if test "${MISSING}" = "" ; then
  2848.     echo You have unpacked all 13 archives.
  2849.     echo "Now read the README and INSTALL files for further instructions."
  2850.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2851. else
  2852.     echo You still need to unpack the following archives:
  2853.     echo "        " ${MISSING}
  2854. fi
  2855. ##  End of shell archive.
  2856. exit 0
  2857. -- 
  2858.     ///  Ti Kan                vorsprung durch technik
  2859.    ///   AMB Research Laboratories, Sunnyvale, CA. USA
  2860.   ///    ti@amb.org
  2861.  //////  ...!{decwrl,synopsys,tandem,tsoft,ultra}!sgiblab!bazooka!ti
  2862. ///      ...!uunet!bazooka!ti
  2863.  
  2864.  
  2865.  
  2866. exit 0 # Just in case...
  2867. -- 
  2868.   // chris@Sterling.COM           | Send comp.sources.x submissions to:
  2869. \X/  Amiga: The only way to fly!  |    sources-x@sterling.com
  2870.        "It's intuitively obvious to the most casual observer..."
  2871.  GCS d++(--) -p+ c++ !l u++ e+ m+(-) s++/++ n h--- f+ g+++ w+ t++ r+ y+
  2872.