home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / problem1 / part07 < prev    next >
Encoding:
Text File  |  1992-11-12  |  37.6 KB  |  1,330 lines

  1. Newsgroups: comp.sources.misc
  2. From: lijewski@rosserv.gsfc.nasa.gov (Mike Lijewski)
  3. Subject:  v33i078:  problem1.1 - A Problem Database Manager, Part07/07
  4. Message-ID: <1992Nov12.195623.29301@sparky.imd.sterling.com>
  5. X-Md4-Signature: ec05e6a723a2d76d1bd57a490ae1b7aa
  6. Date: Thu, 12 Nov 1992 19:56:23 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: lijewski@rosserv.gsfc.nasa.gov (Mike Lijewski)
  10. Posting-number: Volume 33, Issue 78
  11. Archive-name: problem1.1/part07
  12. Environment: UNIX, C++, GDBM, Termcap
  13. Supersedes: problem: Volume 33, Issue 2-9
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 7 (of 7)."
  22. # Contents:  utilities.C
  23. # Wrapped by lijewski@xtesoc2 on Wed Nov 11 16:20:14 1992
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'utilities.C' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'utilities.C'\"
  27. else
  28. echo shar: Extracting \"'utilities.C'\" \(35328 characters\)
  29. sed "s/^X//" >'utilities.C' <<'END_OF_FILE'
  30. X/*
  31. X** utilities.C - utility functions
  32. X**
  33. X** utilities.C utilities.C 1.65   Delta\'d: 08:41:30 11/4/92   Mike Lijewski, CNSF
  34. X**
  35. X** Copyright \(c\) 1991, 1992 Cornell University
  36. X** All rights reserved.
  37. X**
  38. X** Redistribution and use in source and binary forms are permitted
  39. X** provided that: \(1\) source distributions retain this entire copyright
  40. X** notice and comment, and \(2\) distributions including binaries display
  41. X** the following acknowledgement:  ``This product includes software
  42. X** developed by Cornell University\'\' in the documentation or other
  43. X** materials provided with the distribution and in all advertising
  44. X** materials mentioning features or use of this software. Neither the
  45. X** name of the University nor the names of its contributors may be used
  46. X** to endorse or promote products derived from this software without
  47. X** specific prior written permission.
  48. X**
  49. X** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
  50. X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  51. X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  52. X*/
  53. X
  54. X#include <ctype.h>
  55. X
  56. X#ifndef _IBMR2
  57. X#include <libc.h>
  58. X#endif
  59. X
  60. X#include <fcntl.h>
  61. X
  62. X#include <osfcn.h>
  63. X#include <pwd.h>
  64. X#include <signal.h>
  65. X#include <stdarg.h>
  66. X#include <stdlib.h>
  67. X#include <stdio.h>
  68. X
  69. X#ifndef _IBMR2
  70. X#include <unistd.h>
  71. X#endif
  72. X
  73. X// access is prototyped in <sys/access.h> on RS/6000s
  74. X#ifdef _IBMR2
  75. X#include <sys/access.h>
  76. X#include <sys/lockf.h>
  77. X#endif
  78. X
  79. X#include <sys/stat.h>
  80. X#include <sys/types.h>
  81. X#ifdef ESIX
  82. Xtypedef int pid_t;
  83. X#endif
  84. X#include <sys/wait.h>
  85. X#include <string.h>
  86. X#include <sys/errno.h>
  87. X
  88. X// this header file is badly busted on RS/6000s
  89. X#ifndef _IBMR2
  90. X#include <unistd.h>
  91. X#endif
  92. X
  93. X#if defined FLOCK || !defined(R_OK)
  94. X#include <sys/file.h>
  95. X#endif
  96. X
  97. X#include "classes.h"
  98. X#include "display.h"
  99. X#include "lister.h"
  100. X#include "problem.h"
  101. X#include "utilities.h"
  102. X
  103. X/* remove this once GNU gets it fixed -- this stuff should be in <unistd.h> */
  104. X#ifdef __GNUG__
  105. Xextern "C" int lockf(int, int, long);
  106. Xextern "C" int flock(int, int);
  107. X#ifndef LOCK_UN
  108. X#define LOCK_UN 8
  109. X#endif
  110. X#ifndef LOCK_EX
  111. X#define LOCK_EX 2
  112. X#endif
  113. X#ifndef F_LOCK
  114. X#define F_LOCK   1
  115. X#endif
  116. X#ifndef F_ULOCK
  117. X#define F_ULOCK  0
  118. X#endif
  119. X#endif /*__GNUG__*/
  120. X
  121. Xconst char KEY_CR    = '\r';   // carriage return
  122. Xconst char KEY_BKSP  = '\b';   // backspace
  123. Xconst char KEY_CTL_L = '\f';   // repaint screen -- CTR-L
  124. Xconst char KEY_DEL   = 127;    // ASCII DELETE
  125. X
  126. X/*
  127. X** fgetline - returns a pointer to the start of a line read from fp,
  128. X**            or the null pointer if we hit eof or get an error from
  129. X**            fgets. Exits if new fails. Strips the newline from
  130. X**            the line. Caller should free memory if desired. size
  131. X**            is the expected length of the line to be read.
  132. X*/
  133. X
  134. Xchar *fgetline(FILE *fp, int size)
  135. X{
  136. X    char *buffer = new char[size];
  137. X
  138. X    char *result= fgets(buffer, size, fp);
  139. X    if (result == 0)
  140. X    {
  141. X        //
  142. X        // Either error or at eof.
  143. X        //
  144. X        DELETE buffer;
  145. X        return 0;
  146. X    }
  147. X
  148. X    if (buffer[strlen(buffer) - 1] != '\n' && !feof(fp))
  149. X    {
  150. X        //
  151. X        // Longer line than buffer can hold.
  152. X        //
  153. X        char *restofline = fgetline(fp, size);
  154. X
  155. X        if (restofline == 0) return 0; // eof or error
  156. X
  157. X        char *longline = new char[strlen(buffer) + strlen(restofline) + 1];
  158. X        (void)strcat(strcpy(longline, buffer), restofline);
  159. X
  160. X        DELETE restofline;
  161. X        DELETE buffer;
  162. X
  163. X        if (longline[strlen(longline) - 1] == '\n')
  164. X            longline[strlen(longline) - 1] = 0;
  165. X
  166. X        return longline;
  167. X    }
  168. X    else
  169. X    {
  170. X        if (buffer[strlen(buffer) - 1] == '\n')
  171. X            buffer[strlen(buffer) - 1] = 0;
  172. X        return buffer;
  173. X    }
  174. X}
  175. X
  176. X/*
  177. X** display_string - prints a string to the given the display, guaranteeing not
  178. X**                  to print more than columns characters.  If the string
  179. X**                  exceeds the width of the window, a ! is placed in
  180. X**                  the final column.  len is the length of the string,
  181. X**                  if known, which defaults to zero. offset, which
  182. X**                  defaults to zero, is non-zero in those cases where we have
  183. X**                  already printed offset characters to the screen line.
  184. X**                  We never call this when trying to write to the last
  185. X**                  row on the screen.  That is the dominion of message.
  186. X*/
  187. X
  188. Xvoid display_string(const char *str, int length, int offset)
  189. X{
  190. X    int len = (length == 0 ? (int) strlen(str) : length) + offset;
  191. X
  192. X    if (len < columns())
  193. X    {
  194. X        (void)fputs(str, stdout);
  195. X        cursor_wrap();
  196. X    }
  197. X    else if (len > columns())
  198. X    {
  199. X        (void)printf("%*.*s%c", columns() - offset - 1, columns() - offset - 1,
  200. X                     str, '!');
  201. X        if (!AM || XN) cursor_wrap();
  202. X    }
  203. X    else
  204. X    {
  205. X        (void)fputs(str, stdout);
  206. X        if (!AM || XN) cursor_wrap();
  207. X    }
  208. X}
  209. X
  210. X/*
  211. X** error - Prints error message so it can be read.  This is the error
  212. X**         function we call once we have initialized the display.
  213. X*/
  214. X
  215. Xvoid error(const char *format, ...)
  216. X{
  217. X    va_list ap;
  218. X    va_start(ap, format);
  219. X    move_to_message_line();
  220. X    clear_to_end_of_line();
  221. X    deinit_screen_and_kbdr();
  222. X    (void) vfprintf(stdout, format, ap);
  223. X    putchar('\n');
  224. X    va_end(ap);
  225. X    exit(1);
  226. X}
  227. X
  228. X/*
  229. X** execute - executes command using exec.  Returns 1 if the exec
  230. X**           went OK, otherwise it returns 0.  Assumes that the execd
  231. X**           program wants our open file descriptors.
  232. X**           Forces the effective uid to the real uid in the child.
  233. X**           If prompt is true, we prompt for a keypress before returning.
  234. X**           Prompt defaults to false.
  235. X*/
  236. X
  237. Xint execute(const char *file, const char *argv[], int prompt)
  238. X{
  239. X    deinit_screen_and_kbdr();
  240. X    unset_signals();
  241. X
  242. X    int status;
  243. X    pid_t pid = fork();
  244. X
  245. X    switch(pid)
  246. X    {
  247. X      case -1:
  248. X        //
  249. X        // error
  250. X        //
  251. X        return 0;
  252. X      case 0:
  253. X        //
  254. X        // in the child
  255. X        //
  256. X        if (setuid(getuid()) < 0)
  257. X            error("file %s, line %d, setuid() failed" __FILE__, __LINE__);
  258. X
  259. X        execvp(file, (char *const *)argv);
  260. X
  261. X        //
  262. X        // exec failed
  263. X        //
  264. X        exit(1);
  265. X      default:
  266. X        //
  267. X        // in the parent
  268. X        //
  269. X#ifdef NOWAITPID
  270. X        while (wait(&status) != pid) ;
  271. X#else
  272. X        waitpid(pid, &status, 0);
  273. X#endif
  274. X
  275. X        set_signals();
  276. X        setraw();
  277. X
  278. X        if (prompt)
  279. X            //
  280. X            // eat a character  -- print in standout mode
  281. X            //
  282. X            (void)yes_or_no("Press Any Key to Continue", 0, Yes, 1);
  283. X        
  284. X        enter_cursor_addressing_mode();
  285. X        enter_visual_mode();
  286. X        enable_keypad();
  287. X        synch_display();
  288. X
  289. X        return status == 0 ? 1 : 0;
  290. X    }
  291. X}
  292. X
  293. X#ifdef SIGWINCH
  294. X
  295. X/*
  296. X** winch - set flag indicating window size changed.
  297. X*/
  298. X
  299. Xint windowSizeChanged;  // should be a sig_atomic_t
  300. X
  301. Xvoid winch(int)
  302. X{
  303. X    (void)signal(SIGWINCH, SIG_IGN);
  304. X    windowSizeChanged = 1;
  305. X    (void)signal(SIGWINCH, winch);
  306. X}
  307. X
  308. X#ifdef M_UNIX
  309. X#include <sys/unistd.h>
  310. X#include <sys/stream.h>
  311. X#include <sys/ptem.h>
  312. X#endif
  313. X
  314. X#include <sys/ioctl.h>
  315. X
  316. X/*
  317. X** adjust_window - called to adjust our window after getting a SIGWINCH
  318. X*/
  319. X
  320. Xvoid adjust_window()
  321. X{
  322. X#ifdef TIOCGWINSZ
  323. X    struct winsize w;
  324. X    if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0)
  325. X        LI = w.ws_row;
  326. X    if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0)
  327. X        CO = w.ws_col;
  328. X#endif
  329. X    if (LI < 5 || CO < 20)
  330. X        error("screen too small to be useful");
  331. X}
  332. X
  333. X#endif
  334. X
  335. X/*
  336. X** prompt - displays msg prompt and then collects the response.
  337. X**          The keys of the response are echoed as they are collected.
  338. X**          The response should be deleted when no longer needed.
  339. X**          A response can contain any graphical character. Backspace
  340. X**          works as expected. Carriage return indicates the end of
  341. X**          response. Non-graphical characters are ignored.  If we
  342. X**          get suspended and resumed in prompt, redisplay is
  343. X**          the function to call to fixed up all but the message line
  344. X**          of the display.  We rely on signals interrupting read.
  345. X*/
  346. X
  347. Xchar *prompt(const char *msg, void (*redisplay)())
  348. X{
  349. X    size_t written = 0;  // number of characters written to message line
  350. X    size_t len = strlen(msg);
  351. X    String nmsg(msg);
  352. X
  353. X    clear_message_line();
  354. X
  355. X    if (len < columns())
  356. X    {
  357. X        (void)fputs(nmsg, stdout);
  358. X        written = len;
  359. X    }
  360. X    else
  361. X    {
  362. X        //
  363. X        // Leave space for columns2 + 1 characters.
  364. X        //
  365. X        (void)fputs((const char *)nmsg + (len-columns()/2+1), stdout);
  366. X        written = columns()/2 - 1;
  367. X    }
  368. X    synch_display();
  369. X
  370. X    //
  371. X    // We never echo into the last position in the message window.
  372. X    //
  373. X    size_t space_available = columns() - written; // available spaces in line
  374. X    char *response = new char[space_available + 1];
  375. X    size_t pos = 0;  // index of next character in response
  376. X
  377. X    char key;
  378. X    for (;;)
  379. X    {
  380. X        if (resumingAfterSuspension ||
  381. X#ifdef SIGWINCH
  382. X            windowSizeChanged       ||
  383. X#endif
  384. X            read(0, &key, 1) < 0    || // assume only fails when errno == EINTR
  385. X            key == KEY_CTL_L)
  386. X        {
  387. X#ifdef SIGWINCH
  388. X            if (windowSizeChanged)
  389. X            {
  390. X                windowSizeChanged = 0;
  391. X                adjust_window();
  392. X            }
  393. X#endif
  394. X            resumingAfterSuspension = 0;
  395. X            redisplay();
  396. X            clear_message_line();  // make sure we are on the message line
  397. X            response[pos] = 0;
  398. X            if (pos + len < columns())
  399. X            {
  400. X                //
  401. X                // Output message and response-to-date.
  402. X                //
  403. X                (void)fputs(nmsg, stdout);
  404. X                (void)fputs(response, stdout);
  405. X                space_available = columns() - pos - len;
  406. X            }
  407. X            else if (pos < columns())
  408. X            {
  409. X                //
  410. X                // Display the response.
  411. X                //
  412. X                (void)fputs(response, stdout);
  413. X                space_available = columns() - strlen(response);
  414. X            }
  415. X            else
  416. X            {
  417. X                //
  418. X                // Display the backend of the response.
  419. X                //
  420. X                (void)fputs(&response[pos - columns()/2 + 1], stdout);
  421. X                space_available = columns()/2 + 1;
  422. X            }
  423. X            synch_display();
  424. X        }
  425. X        else if (isprint(key))
  426. X        {
  427. X            //
  428. X            // echo character to message window and wait for another
  429. X            //
  430. X            response[pos++] = key;
  431. X            space_available--;
  432. X            if (!space_available)
  433. X            {
  434. X                //
  435. X                // Need to allocate more room for the response.
  436. X                // Note that strlen\(response\) == pos
  437. X                //
  438. X                space_available = columns()/2 + 1;
  439. X                char *nresponse = new char[pos + space_available + 1];
  440. X                response[pos] = 0;  // stringify response
  441. X                (void)strcpy(nresponse, response);
  442. X
  443. X                DELETE response;
  444. X                response = nresponse;
  445. X
  446. X                //
  447. X                // Shift prompt in message window so we
  448. X                // always have the end in view to which we are
  449. X                // adding characters as they are typed.
  450. X                //
  451. X                clear_message_line();
  452. X                (void)fputs(&response[pos - columns()/2 + 1], stdout);
  453. X                key = 0;  // nullify key
  454. X            }
  455. X            else
  456. X            {
  457. X                putchar(key);
  458. X                key = 0;  // nullify key
  459. X            }
  460. X            synch_display();
  461. X        }
  462. X        else
  463. X            switch (key)
  464. X            {
  465. X              case KEY_CR: // we have the complete response
  466. X                response[pos] = 0;
  467. X                clear_message_line();
  468. X                synch_display();
  469. X                return response;
  470. X              case KEY_DEL:
  471. X              case KEY_BKSP: // back up one character
  472. X                if (pos == 0)
  473. X                {
  474. X                    ding();
  475. X                    break;
  476. X                }
  477. X                backspace();
  478. X                DC ? delete_char_at_cursor() : clear_to_end_of_line();
  479. X                --pos;
  480. X                ++space_available;
  481. X                if (space_available == columns())
  482. X                {
  483. X                    //
  484. X                    // The only way this can happen is if we
  485. X                    // had previously shifted the response to the left.
  486. X                    // Now we must shift the response to the right.
  487. X                    //
  488. X                    clear_message_line();
  489. X                    response[pos] = 0;
  490. X                    if (pos + len < columns())
  491. X                    {
  492. X                        //
  493. X                        // Output message and response-to-date.
  494. X                        //
  495. X                        (void)fputs(nmsg, stdout);
  496. X                        (void)fputs(response, stdout);
  497. X                        space_available = columns() - pos - len;
  498. X                    }
  499. X                    else if (pos < columns())
  500. X                    {
  501. X                        //
  502. X                        // Display the response.
  503. X                        //
  504. X                        (void)fputs(response, stdout);
  505. X                        space_available = columns() - strlen(response);
  506. X                    }
  507. X                    else
  508. X                    {
  509. X                        //
  510. X                        // Display the backend of the response
  511. X                        //
  512. X                        (void)fputs(&response[pos - columns()/2 + 1], stdout);
  513. X                        space_available = columns()/2 + 1;
  514. X                    }
  515. X                }
  516. X                synch_display();
  517. X                break;
  518. X              default: ding(); break; // ignore other characters
  519. X            }
  520. X    }
  521. X}
  522. X
  523. X/*
  524. X** message - prints a message on the last line of the screen.
  525. X**           It is up to the calling process to put the cursor
  526. X**           back where it belongs.  Synchs the display.  It can
  527. X**           be called as either:
  528. X**
  529. X**                message\(msg\);
  530. X**           or
  531. X**                message\(fmt, str\);
  532. X**
  533. X**           In the later case it must be the case that the format fmt
  534. X**           has exactly one % into which the str will be substituted
  535. X**           as in the ?printf functions.  Prints in standout mode.
  536. X*/
  537. X
  538. X//
  539. X// the definition -- declared in utilities.h
  540. X//
  541. Xint message_window_dirty = 0;
  542. X
  543. Xvoid message(const char *fmt, const char *str)
  544. X{
  545. X    String msg;          // the complete message to be output
  546. X
  547. X    clear_message_line();
  548. X
  549. X    if (str)
  550. X    {
  551. X        const char *token = strchr(fmt, '%');
  552. X        if (token == 0)
  553. X            //
  554. X            // This should not happen.  But if it does, let us
  555. X            // just print the format fmt.
  556. X            //
  557. X            msg = fmt;
  558. X        else
  559. X        {
  560. X            msg  = String(fmt, token - fmt);
  561. X            msg += str;
  562. X            msg += token + 1;
  563. X        }
  564. X    }
  565. X    else
  566. X        msg = fmt;
  567. X
  568. X    if (msg.length() < columns())
  569. X        (void)fputs(msg, stdout);
  570. X    else
  571. X        (void)printf("%*.*s", columns() - 1, columns() - 1, (const char *)msg);
  572. X
  573. X    synch_display();
  574. X    message_window_dirty = 1;
  575. X}
  576. X
  577. X/*
  578. X** yes_or_no - returns true if a y or Y is typed in response to
  579. X**             the msg. We deal with being suspended and resumed.
  580. X**
  581. X**             defResponse is the assumed default response.
  582. X**
  583. X**                 defResponse == Yes ==> that return value is true unless
  584. X**                                        n or N is typed.
  585. X**
  586. X**                 defResponse == No  ==> that return value is true only if
  587. X**                                        y or Y is typed; else it
  588. X**                                        return false.
  589. X**
  590. X**             If standout is true the message is displayed in standout mode.
  591. X**
  592. X**             It is assumed that the message that is printed somehow indicates
  593. X**             whether the default response is true or false.
  594. X*/
  595. X
  596. Xint yes_or_no(const char *msg,
  597. X              void (*redisplay)(),
  598. X              Response defResponse,
  599. X              int standout)
  600. X{
  601. X    if (standout) enter_standout_mode();
  602. X    message(msg);
  603. X    if (standout) end_standout_mode();
  604. X
  605. X    char key;
  606. X    while (1)
  607. X        //
  608. X        // read a character dealing with interruption
  609. X        //
  610. X        if (resumingAfterSuspension ||
  611. X#ifdef SIGWINCH
  612. X            windowSizeChanged       ||
  613. X#endif
  614. X            read(0, &key, 1) < 0    || // assume only fails when errno==EINTR 
  615. X            key == KEY_CTL_L)
  616. X        {
  617. X#ifdef SIGWINCH
  618. X            if (windowSizeChanged)
  619. X            {
  620. X                windowSizeChanged = 0;
  621. X                adjust_window();
  622. X            }
  623. X#endif
  624. X            resumingAfterSuspension = 0;
  625. X            if (redisplay) redisplay();
  626. X            if (standout) enter_standout_mode();
  627. X            message(msg);
  628. X            if (standout) end_standout_mode();
  629. X        }
  630. X        else
  631. X            break;
  632. X
  633. X    clear_message_line();
  634. X    synch_display();
  635. X
  636. X    switch (defResponse)
  637. X    {
  638. X      case Yes:
  639. X        return !(key == 'n' || key == 'N');
  640. X      case No:
  641. X        return   key == 'y' || key == 'Y';
  642. X    }
  643. X}
  644. X
  645. X/*
  646. X** lock_file - lock the file opened with file descriptor fd.
  647. X**             Exits on error.
  648. X*/
  649. X
  650. Xvoid lock_file(int fd)
  651. X{
  652. X    if (lseek(fd, 0, 0) < 0) error("lseek() failed");
  653. X#ifdef FLOCK
  654. X    if (flock(fd, LOCK_EX) < 0) error("flock - LOCK_EX");
  655. X#else
  656. X    if (lockf(fd, F_LOCK, 0) < 0) error("lockf - F_LOCK");
  657. X#endif
  658. X}
  659. X
  660. X/*
  661. X** unlock_file - unlock file with file descriptor fd.
  662. X**               Exits on error.
  663. X*/
  664. X
  665. Xvoid unlock_file(int fd)
  666. X{
  667. X    if (lseek(fd, 0, 0) < 0) error("lseek() failed");
  668. X#ifdef FLOCK
  669. X    if (flock(fd, LOCK_UN) < 0) error("flock - LOCK_UN");
  670. X#else
  671. X    if (lockf(fd, F_ULOCK, 0) < 0) error("lockf - F_ULOCK");
  672. X#endif
  673. X}
  674. X
  675. X/*
  676. X** quit - cleanup and exit.  Called after a SIGHUP, SIGTERM, SIGQUIT
  677. X**        or SIGINT, or on normal termination.  sig defaults to 0.
  678. X*/
  679. X
  680. Xvoid quit(int sig) { deinit_screen_and_kbdr(); exit(sig); }
  681. X
  682. X/*
  683. X** username - returns the username pertaining to the real uid.
  684. X**            Exits on error;
  685. X*/
  686. X
  687. Xconst char *username()
  688. X{
  689. X    static String user;
  690. X    if (user == "")
  691. X    {
  692. X        struct passwd *entry = getpwuid(getuid());
  693. X        if (!entry)
  694. X            error("file %s, line %d, getpwuid() failed", __FILE__, __LINE__);
  695. X        user =  entry->pw_name;
  696. X    }
  697. X    return user;
  698. X}
  699. X
  700. X/*
  701. X** write_to_pipe - writes the data to the pipe on the file descriptor.
  702. X**                 Exits on error. 
  703. X*/
  704. X
  705. Xvoid write_to_pipe(int fd, const char *data, int size)
  706. X{
  707. X    int nwritten = 0;
  708. X    while (size > 0)
  709. X    {
  710. X        nwritten = write(fd, data, size);
  711. X        if (nwritten <= 0)
  712. X            error("file %s, line %d, write() failed", __FILE__, __LINE__);
  713. X        size -= nwritten;
  714. X        data += nwritten;
  715. X    }
  716. X
  717. X}
  718. X
  719. X/*
  720. X** read_file - reads the file pointed to by fp into the array lines.
  721. X**             lines must have been previously allocated in the caller,
  722. X**             to size size. len is the expected length of the lines
  723. X**             in the file. pos is the position in lines
  724. X**             to which we start adding the lines; it defaults to zero.
  725. X**             In general, this will be zero, but in one case when I am
  726. X**             building an argv for an execv, I want a non-zero offset.
  727. X**             Returns the number of lines in lines or -1 on error.
  728. X**             lines is null-terminated.
  729. X**
  730. X**             This is used only for reading files containing 
  731. X**             relatively few lines.  It skips over lines with a "#"
  732. X**             in the first column and blank lines.
  733. X*/
  734. X
  735. Xint read_file(FILE *fp, char** &lines, int size, int linelen, int pos)
  736. X{
  737. X    const int chunksize = 20;  // chunksize to grow by
  738. X    int nlines = 0;            // number of lines added to lines
  739. X
  740. X    char *line = fgetline(fp, linelen);
  741. X    for (; line; line = fgetline(fp, linelen))
  742. X    {
  743. X        //
  744. X        // Skip comment lines and empty lines.
  745. X        //
  746. X        if (*line == '#' || strcmp(line, "") ==  0)
  747. X        {
  748. X            DELETE line;
  749. X            continue;
  750. X        }
  751. X
  752. X        //
  753. X        // strip off newline
  754. X        //
  755. X        if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
  756. X        lines[pos++] = line;
  757. X        nlines++;
  758. X        if (pos == size)
  759. X        {
  760. X            //
  761. X            // grow Areas
  762. X            //
  763. X            char **newspace = new char*[size += chunksize];
  764. X            for (int i = 0; i < pos; i++) newspace[i] = lines[i];
  765. X            DELETE lines;
  766. X            lines = newspace;
  767. X        }
  768. X    }
  769. X    if (feof(fp) && !ferror(fp))
  770. X    {
  771. X        //
  772. X        // null terminate lines
  773. X        //
  774. X        if (pos == size)
  775. X        {
  776. X            //
  777. X            // got to grow one more
  778. X            //
  779. X            char **newspace = new char*[size + 1];
  780. X            for (int i = 0; i < pos; i++) newspace[i] = lines[i];
  781. X            DELETE lines;
  782. X            lines = newspace;
  783. X        }
  784. X        lines[pos] = 0;
  785. X        return nlines;
  786. X    }
  787. X    else
  788. X        return -1;
  789. X}
  790. X
  791. X/*
  792. X** tokenize - returns a null-terminated vector of the words in line
  793. X**            The vector and its elements are in volatile storage
  794. X**            which we manage here.
  795. X*/
  796. X
  797. Xconst char **tokenize(const char *line, const char *separators)
  798. X{
  799. X    //
  800. X    // Since strtok modifies its argument, we use a copy of line.
  801. X    //
  802. X    static char *newline;     // volatile storage of vector elements
  803. X    DELETE newline;
  804. X    newline = new char[strlen(line) + 1];
  805. X    (void)strcpy(newline, line);
  806. X
  807. X    const int chunksize = 5;  // chunksize to grow by
  808. X    int size   = chunksize;   // total size of vector
  809. X    int nwords = 0;           // number of words in vector
  810. X    static char **words;      // volatile storage for the word pointers
  811. X    DELETE words;
  812. X    words = new char*[chunksize];
  813. X
  814. X    if ((words[nwords++] = strtok(newline, separators)) == 0)
  815. X        return (const char **)words;
  816. X
  817. X    while (words[nwords++] = strtok(0, separators))
  818. X        if (nwords == size)
  819. X        {
  820. X            //
  821. X            // Grow words.
  822. X            //
  823. X            char **newspace = new char*[size += chunksize];
  824. X            for (int i = 0; i < nwords; i++) newspace[i] = words[i];
  825. X            DELETE words;
  826. X            words = newspace;
  827. X        }
  828. X    return (const char **)words;
  829. X}
  830. X
  831. X/*
  832. X** read_and_exec_perm - returns non-zero if we have read and execute
  833. X**                      permission on the file, otherwise 0.
  834. X**                      Returns 0 on error.
  835. X*/
  836. X
  837. Xint read_and_exec_perm(const char *file)
  838. X{
  839. X    return access(file, R_OK | X_OK) == -1 ? 0 : 1;
  840. X}
  841. X
  842. X/*
  843. X** expand_tilde - expects a string of the form "~ ...".
  844. X**                Returns a new string in volatile storage
  845. X**                with the user\'s home directory in place of the ~.
  846. X**                The user\'s home directory is always appended
  847. X**                in the form: "/usr/staff/mjlx"; a slash is not added to
  848. X**                the end of the home directory string.  Returns the original
  849. X**                string if we cannot get the user\'s home directory.
  850. X*/
  851. X
  852. Xconst char *expand_tilde(char *str)
  853. X{
  854. X    static char *home = getenv("HOME");
  855. X    if (home == NULL)
  856. X    {
  857. X        struct passwd *user = getpwuid(getuid());
  858. X        if (user == NULL) return str;
  859. X        home = user->pw_dir;
  860. X    }
  861. X    if (*str != '~') return str;
  862. X    static String expansion;
  863. X    expansion  = home;
  864. X    expansion += (str + 1);
  865. X    return expansion;
  866. X}
  867. X
  868. X/*
  869. X** update_screen_line
  870. X**
  871. X**     oldline is what is currently on the screen in row y
  872. X**     newline is what we want on the screen in row y
  873. X**
  874. X**     We make a good attempt to optimize the output of characters to
  875. X**     the screen.  We want to display newline on the screen,
  876. X**     assuming oldline is what is currently displayed.  This
  877. X**     will be "good" if oldline and newline are quite similar.
  878. X**     That is to say, this should only be called when there is an
  879. X**     expectation that oldline and newline are "almost" the same.
  880. X*/
  881. X
  882. Xvoid update_screen_line(const char *oldline, const char *newline, int y)
  883. X{
  884. X    if (strcmp(oldline, newline) == 0) return;
  885. X
  886. X    size_t olen = strlen(oldline);
  887. X    size_t nlen = strlen(newline);
  888. X    size_t  len = olen < nlen ? olen : nlen;
  889. X
  890. X    //
  891. X    // Never display more than columns characters.
  892. X    //
  893. X    int chop = 0;  // do we need to chop off the tail?
  894. X    if (len > columns()) { chop = 1; len = columns(); }
  895. X
  896. X    char *equal = new char[len];
  897. X
  898. X    //
  899. X    // How similar are the two strings?
  900. X    //
  901. X    int differences = 0;
  902. X    for (int i = 0; i < len; i++) equal[i] = 1;
  903. X    for (i = 0; i < len; i++)
  904. X        if (oldline[i] != newline[i]) { differences++; equal[i] = 0; }
  905. X
  906. X    if (differences > columns()/2)
  907. X    {
  908. X        //
  909. X        // We just display the new line.
  910. X        //
  911. X        clear_to_end_of_line();
  912. X        (void)fputs(newline, stdout);
  913. X        DELETE equal;
  914. X        return;
  915. X    }
  916. X
  917. X    if (!OS)
  918. X    {
  919. X        //
  920. X        // We can just overwrite the old with the new.
  921. X        //
  922. X        int last = -2;  // position of last character written
  923. X        for (i = 0; i < len; i++)
  924. X        {
  925. X            if (equal[i]) continue;
  926. X            if (i - 1 != last) move_cursor(y, i);
  927. X            (i == len - 1 && chop) ? putchar('!') : putchar(newline[i]);
  928. X            last = i;
  929. X        }
  930. X        if (nlen > olen)
  931. X        {
  932. X            //
  933. X            // Have more characters to output.
  934. X            //
  935. X            chop = len > columns();
  936. X            move_cursor(y, i);
  937. X            for (i = (int)len; i < nlen && i < columns(); i++)
  938. X                (i == columns()-1 && chop) ? putchar('!') : putchar(newline[i]);
  939. X        }
  940. X        else if (nlen < olen)
  941. X        {
  942. X            move_cursor(y, i);
  943. X            clear_to_end_of_line();
  944. X        }
  945. X    }
  946. X    else
  947. X    {
  948. X        //
  949. X        // We can not overwrite.  Truncate at first difference.
  950. X        //
  951. X        int first = 0;
  952. X        for (i = 0; i < len; i++)
  953. X            if (!equal[i])
  954. X            {
  955. X                first = i;
  956. X                break;
  957. X            }
  958. X        move_cursor(y, i);
  959. X        clear_to_end_of_line();
  960. X        for (; i < nlen && i < columns(); i++)
  961. X            (i == columns() - 1) ? putchar('!') : putchar(newline[i]);
  962. X    }
  963. X    DELETE equal;
  964. X}
  965. X
  966. X/*
  967. X** update_modeline - this routine concatenates the two strings
  968. X**                   into the modeline.  The modeline
  969. X**                   is displayed in standout mode if possible.
  970. X**                   We never put more than columns characters into
  971. X**                   the modeline.  The modeline is the penultimate
  972. X**                   line on the terminal screen.  It does not
  973. X**                   synch the display.  If head == tail == 0, we
  974. X**                   just display the old modeline.  This happens
  975. X**                   if for some reason we had to clear the screen.
  976. X*/
  977. X
  978. X//
  979. X// the current modeline
  980. X//
  981. Xchar *current_modeline;
  982. X
  983. Xvoid update_modeline(const char *head, const char *tail)
  984. X{
  985. X    move_to_modeline();
  986. X    enter_standout_mode();
  987. X
  988. X    if (head == 0)   // actually, head == tail == 0
  989. X    {
  990. X        //
  991. X        // Redisplay old modeline.
  992. X        //
  993. X        (void)fputs(current_modeline, stdout);
  994. X        end_standout_mode();
  995. X        return;
  996. X    }
  997. X
  998. X    int len = (int) strlen(head);
  999. X    char *new_modeline = new char[columns() + 1];
  1000. X    (void)strncpy(new_modeline, head, columns());
  1001. X    new_modeline[columns()] = 0;  // ensure it is null-terminated
  1002. X    
  1003. X    if (len < columns())
  1004. X    {
  1005. X        //
  1006. X        // Write exactly columns characters to modeline.
  1007. X        //
  1008. X        for (int i = len; i < columns() - 1 && tail && *tail; i++, tail++)
  1009. X            new_modeline[i] = *tail;
  1010. X        if (i < columns() - 1)
  1011. X        {
  1012. X            new_modeline[i++] = ' ';
  1013. X            for (; i < columns(); i++) new_modeline[i] = '-';
  1014. X        }
  1015. X        else if (tail && *tail)
  1016. X            //
  1017. X            // The tail was overly long.  Put a ! in the last space
  1018. X            // on the modeline to signify truncation.
  1019. X            //
  1020. X            new_modeline[columns() - 1] = '!';
  1021. X        else
  1022. X            //
  1023. X            // Here len == columns-1 && there is nothing else in tail.
  1024. X            //
  1025. X            new_modeline[columns() - 1] = ' ';
  1026. X    }
  1027. X    else if (len > columns())
  1028. X        new_modeline[columns() - 1] = '!';
  1029. X
  1030. X    if (current_modeline)
  1031. X    {
  1032. X        update_screen_line(current_modeline, new_modeline, rows() - 2);
  1033. X        DELETE current_modeline;
  1034. X    }
  1035. X    else
  1036. X        (void)fputs(new_modeline, stdout);
  1037. X
  1038. X    current_modeline = new_modeline;
  1039. X    end_standout_mode();
  1040. X}
  1041. X
  1042. X/*
  1043. X** lines_displayed - returns the number of lines in the DList
  1044. X**                   currently displayed on the screen.
  1045. X*/
  1046. X
  1047. Xint lines_displayed(DList *dl)
  1048. X{
  1049. X    DLink *ln = dl->firstLine();
  1050. X    for (int i = 1; ln != dl->lastLine(); i++, ln = ln->next()) ;
  1051. X    return i;
  1052. X}
  1053. X
  1054. X/*
  1055. X** get_problem_number - returns the prob# of the current line of the DList
  1056. X**                      in volatile storage.  The line is of the form:
  1057. X**
  1058. X**                         prob# ...
  1059. X**
  1060. X**                      We know INTIMATELY how summary_lines
  1061. X**                      formats the output.
  1062. X*/
  1063. X
  1064. Xconst char *get_problem_number(const DList *dl)
  1065. X{
  1066. X    static String number; // our volatile storage
  1067. X
  1068. X    const char* begin = dl->currLine()->line();
  1069. X
  1070. X    //
  1071. X    // step over any spaces preceding Prob #
  1072. X    //
  1073. X    while (*begin == ' ') begin++;
  1074. X
  1075. X    number = String(begin, strchr(begin, ' ') - begin);
  1076. X
  1077. X    return (const char *)number;
  1078. X}
  1079. X
  1080. X/*
  1081. X** leftshift_current_line - shifts the current line in DList left until
  1082. X**                          its tail is visible.
  1083. X*/
  1084. X
  1085. Xvoid leftshift_current_line(DList *dl)
  1086. X{
  1087. X    int inc = dl->currLine()->length()-columns()+1;
  1088. X    move_cursor(dl->savedYPos(), 0);
  1089. X    clear_to_end_of_line();
  1090. X    display_string(&(dl->currLine()->line())[inc],columns()-1);
  1091. X    dl->saveYXPos(dl->savedYPos(), max(goal_column(dl)-inc, 0));
  1092. X    move_cursor(dl->savedYPos(), dl->savedXPos());
  1093. X}
  1094. X
  1095. X/*
  1096. X** rightshift_current_line - rightshifts current line to "natural" position.
  1097. X*/
  1098. X
  1099. Xvoid rightshift_current_line(DList *dl)
  1100. X{
  1101. X    move_cursor(dl->savedYPos(), 0);
  1102. X    clear_to_end_of_line();
  1103. X    display_string(dl->currLine()->line(), dl->currLine()->length());
  1104. X    dl->saveYXPos(dl->savedYPos(), goal_column(dl));
  1105. X    move_cursor(dl->savedYPos(), dl->savedXPos());
  1106. X}
  1107. X
  1108. X/*
  1109. X** initial_listing - prints the initial listing screen.
  1110. X**                   Adjusts firstLine, lastLine and currLine.
  1111. X*/
  1112. X
  1113. Xvoid initial_listing(DList *dl)
  1114. X{
  1115. X    DLink *ln = dl->head();
  1116. X    dl->setFirst(ln);
  1117. X    dl->setCurrLine(ln);
  1118. X
  1119. X    clear_display_area();
  1120. X    
  1121. X    for (int i = 0; i < rows() - 2 && ln; ln = ln->next(), i++)
  1122. X        display_string(ln->line(), ln->length());
  1123. X
  1124. X    ln ? dl->setLast(ln->prev()) : dl->setLast(dl->tail());
  1125. X}
  1126. X
  1127. X/*
  1128. X** seconds_in_date - returns the number of seconds in a date string
  1129. X**                   of the form:
  1130. X**
  1131. X**                       "mmm dd mm:hh:ss yyyy"
  1132. X**
  1133. X**                   This is the format returned by ctime, with
  1134. X**                   the leading "day" name chopped off.  According to ANSI C,
  1135. X**                   a long must be able to hold values up to at least
  1136. X**                   2147483647.  This will keep this function working for
  1137. X**                   many years into the future.  Eventually, CurrentYear will
  1138. X**                   have to be incremented.
  1139. X*/
  1140. X
  1141. Xlong seconds_in_date(const char *date)
  1142. X{
  1143. X    //
  1144. X    // days\[i\] is the number of days preceding month i,
  1145. X    // where 0 <= i <= 11
  1146. X    //
  1147. X    static const int days[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
  1148. X
  1149. X    //
  1150. X    // mhash\[*date + *\(date+1\) + *\(date+2\) - 268\] hashes to an integer
  1151. X    // in the range 0-11 signifying the month.
  1152. X    //
  1153. X    static char mhash[] = { 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1154. X                            0, 0, 7, 0, 0, 2, 0, 0, 3, 0, 0, 9, 4, 8, 0,
  1155. X                            0, 6, 0, 5, 0, 0, 0, 0, 0, 10 };
  1156. X
  1157. X    const long SecondsPerDay  = 86400L;
  1158. X    const long SecondsPerYear = 31536000L;
  1159. X    const int CurrentYear = 1992;
  1160. X    const char *const fmt = "%d";
  1161. X    long total = 0;
  1162. X    int tmp, mon = mhash[*date + *(date + 1) + *(date + 2) - 268];
  1163. X
  1164. X    (void)sscanf(date + 13, fmt, &tmp);   // seconds
  1165. X    total += tmp;
  1166. X    (void)sscanf(date + 10, fmt, &tmp);   // minutes
  1167. X    total += tmp * 60;
  1168. X    (void)sscanf(date + 7 , fmt, &tmp);   // hours
  1169. X    total += tmp * 3600;
  1170. X    (void)sscanf(date + 4 , fmt, &tmp);   // day of the month
  1171. X    total += (tmp - 1) * SecondsPerDay;
  1172. X    total += days[mon] * SecondsPerDay;   // days in months preceding this one
  1173. X    (void)sscanf(date + 16, fmt, &tmp);   // the year
  1174. X    // leap year adjustment
  1175. X    if (tmp % 4 == 0 && tmp % 100 != 0 && mon >= 2) total += SecondsPerDay;
  1176. X    total += (tmp - CurrentYear) * SecondsPerYear;
  1177. X    return total;
  1178. X}
  1179. X
  1180. X/*
  1181. X** temporary_file - returns the name of a temporary file.  The temporary
  1182. X**                  is forced to have permission 666.  Note that
  1183. X**                  we force tmpnam to store the name for us in
  1184. X**                  volatile storage.
  1185. X*/
  1186. X
  1187. Xconst char *temporary_file()
  1188. X{
  1189. X    char *file = tmpnam(0);
  1190. X    if (file == 0)
  1191. X        error("file %s, line %d, tmpnam() failed", __FILE__, __LINE__);
  1192. X    int fd;
  1193. X    if ((fd = open(file, O_RDWR|O_CREAT)) < 0)
  1194. X        error("file %s, line %d, open(%s) failed",
  1195. X              __FILE__, __LINE__, file);
  1196. X    if (chmod(file, 0666) < 0)
  1197. X        error("file %s, line %d, chmod(%s) failed",
  1198. X              __FILE__, __LINE__, file);
  1199. X    (void)close(fd);
  1200. X    return file;
  1201. X}
  1202. X
  1203. X/*
  1204. X** set_signals - set up our signal handlers
  1205. X*/
  1206. X
  1207. Xvoid set_signals()
  1208. X{
  1209. X    (void)signal(SIGHUP,  quit);
  1210. X    (void)signal(SIGINT,  quit);
  1211. X    (void)signal(SIGQUIT, quit);
  1212. X    (void)signal(SIGTERM, quit);
  1213. X#ifdef SIGTSTP
  1214. X    (void)signal(SIGTSTP, termstop);
  1215. X#endif
  1216. X#ifdef SIGWINCH
  1217. X    (void)signal(SIGWINCH, winch);
  1218. X#endif
  1219. X}
  1220. X
  1221. X/*
  1222. X** unset_signals - set signals back to defaults
  1223. X*/
  1224. X
  1225. Xvoid unset_signals()
  1226. X{
  1227. X    (void)signal(SIGHUP,  SIG_DFL);
  1228. X    (void)signal(SIGINT,  SIG_DFL);
  1229. X    (void)signal(SIGQUIT, SIG_DFL);
  1230. X    (void)signal(SIGTERM, SIG_DFL);
  1231. X#ifdef SIGTSTP
  1232. X    (void)signal(SIGTSTP, SIG_DFL);
  1233. X#endif
  1234. X#ifdef SIGWINCH
  1235. X    (void)signal(SIGWINCH, SIG_DFL);
  1236. X#endif
  1237. X}
  1238. X
  1239. X/*
  1240. X** block_tstp_and_winch - block SIGTSTP and SIGWINCH
  1241. X*/
  1242. X
  1243. X#ifdef BSDSIGS
  1244. Xstatic int oldmask;
  1245. X#elif POSIXSIGS
  1246. Xstatic sigset_t oldset;
  1247. X#endif
  1248. X
  1249. Xvoid block_tstp_and_winch()
  1250. X{
  1251. X#ifdef BSDSIGS
  1252. X    int oldmask = sigblock(sigmask(SIGTSTP)
  1253. X#ifdef SIGWINCH
  1254. X                           | sigmask(SIGWINCH)
  1255. X#endif
  1256. X                           );
  1257. X#elif POSIXSIGS
  1258. X    sigset_t newset;
  1259. X    sigemptyset(&newset);
  1260. X#ifdef SIGTSTP
  1261. X    sigaddset(&newset, SIGTSTP);
  1262. X#endif
  1263. X#ifdef SIGWINCH
  1264. X    sigaddset(&newset, SIGWINCH);
  1265. X#endif
  1266. X    if (sigprocmask(SIG_BLOCK, &newset, &oldset) < 0)
  1267. X        error("file %s, line %d, sigprocmask(SIG_BLOCK) failed\n",
  1268. X              __FILE__, __LINE__);
  1269. X#else
  1270. X    //
  1271. X    // We use ANSI C signals.  These can be "lost" but it is the
  1272. X    // best we can do.
  1273. X    //
  1274. X#ifdef SIGTSTP
  1275. X    (void)signal(SIGTSTP, SIG_IGN);
  1276. X#endif
  1277. X#ifdef SIGWINCH
  1278. X    (void)signal(SIGWINCH, SIG_IGN);
  1279. X#endif
  1280. X#endif
  1281. X}
  1282. X
  1283. X/*
  1284. X** unblock_tstp_and_winch - unblock SIGTSTP and SIGWINCH
  1285. X*/
  1286. X
  1287. Xvoid unblock_tstp_and_winch()
  1288. X{
  1289. X#ifdef BSDSIGS
  1290. X    (void)sigsetmask(oldmask);
  1291. X#elif POSIXSIGS
  1292. X    if (sigprocmask(SIG_SETMASK, &oldset, 0) < 0)
  1293. X        error("file %s, line %d, sigprocmask(SIG_SETMASK) failed\n",
  1294. X              __FILE__, __LINE__);
  1295. X#else
  1296. X#ifdef SIGTSTP
  1297. X    (void)signal(SIGTSTP, termstop);
  1298. X#endif
  1299. X#ifdef SIGWINCH
  1300. X    (void)signal(SIGWINCH, winch);
  1301. X#endif
  1302. X#endif
  1303. X}
  1304. X
  1305. END_OF_FILE
  1306. if test 35328 -ne `wc -c <'utilities.C'`; then
  1307.     echo shar: \"'utilities.C'\" unpacked with wrong size!
  1308. fi
  1309. # end of 'utilities.C'
  1310. fi
  1311. echo shar: End of archive 7 \(of 7\).
  1312. cp /dev/null ark7isdone
  1313. MISSING=""
  1314. for I in 1 2 3 4 5 6 7 ; do
  1315.     if test ! -f ark${I}isdone ; then
  1316.     MISSING="${MISSING} ${I}"
  1317.     fi
  1318. done
  1319. if test "${MISSING}" = "" ; then
  1320.     echo You have unpacked all 7 archives.
  1321.     rm -f ark[1-9]isdone
  1322. else
  1323.     echo You still need to unpack the following archives:
  1324.     echo "        " ${MISSING}
  1325. fi
  1326. ##  End of shell archive.
  1327. exit 0
  1328.  
  1329. exit 0 # Just in case...
  1330.