home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / sources / misc / 4082 < prev    next >
Encoding:
Text File  |  1992-11-12  |  37.8 KB  |  1,337 lines

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