source: trunk/athena/bin/discuss/server/coreutil.c @ 17452

Revision 17452, 19.0 KB checked in by zacheiss, 23 years ago (diff)
Include unistd.h for lseek prototype and SEEK_* macros
Line 
1/*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *      $Id: coreutil.c,v 1.32 2002-04-06 15:18:55 zacheiss Exp $
10 *
11 *
12 * coreutil.c  -- These contain lower-layer, utility type routines to
13 *                be used by core.  These include things to handle the
14 *                in-memory superblock, and to open & close meetings.
15 *
16 */
17
18#ifndef lint
19#ifdef __STDC__
20const
21#endif
22static char rcsid_coreutil_c[] =
23    "$Id: coreutil.c,v 1.32 2002-04-06 15:18:55 zacheiss Exp $";
24#endif /* lint */
25
26#include <discuss/types.h>
27#include <discuss/dsc_et.h>
28#include "atom.h"
29#include "mtg.h"
30#include <discuss/tfile.h>
31#include <discuss/acl.h>
32#include "internal.h"
33#include "ansi.h"
34#if HAVE_ZEPHYR
35#include <zephyr/zephyr.h>
36#endif
37#include <stdio.h>
38#include <stdlib.h>
39#include <errno.h>
40#include <sys/types.h>
41#include <unistd.h>
42#include <netdb.h>
43#include <sys/file.h>
44#include <sys/stat.h>
45#include <string.h>
46#if HAVE_FCNTL_H
47#include <fcntl.h>
48#endif
49#include <unistd.h>
50
51/* global variables */
52char current_mtg [256] = "";    /* meeting that's opened */
53int u_trn_f,u_control_f,u_acl_f; /* UNIX file descriptors */
54bool nuclear = FALSE;           /* Using atomic reads/writes */
55afile a_control_f = NULL;       /* radioactive file descriptor */
56tfile abort_file = NULL;        /* close this on abort */
57bool  read_lock = FALSE;        /* have lock on u_control_f */
58bool  mtg_swapped = FALSE;      /* current meeting is swapped */
59dsc_acl   *mtg_acl = NULL;      /* current mtg acl */
60int     last_acl_mod;           /* last mod to ACL */
61mtg_super super;
62char *super_chairman;
63char *super_long_name;
64int has_privs = 0;              /* Has privileges (linked) */
65int no_nuke = 0;                /* Don't be atomic (linked) */
66#if HAVE_ZEPHYR
67int use_zephyr = 1;             /* Actually do use Zephyr. */
68#else
69int use_zephyr = 0;
70#endif
71
72
73/* EXTERNAL */
74extern int errno;
75extern char rpc_caller [];
76
77/*
78 *
79 * new_string (s)  --   Routine to create a copy of the given string, using
80 *                      malloc.
81 *
82 */
83char *new_string (s)
84char *s;
85{
86     int len;
87     char *newstr;
88
89     len = strlen (s) + 1;
90     newstr = malloc ((unsigned)len);
91     (void) strcpy (newstr, s);
92     return (newstr);
93}
94
95/*
96 *
97 * open_mtg (mtg_name) -- Routine to open a meeting, if necessary.
98 *
99 */
100int open_mtg (mtg_name)
101char *mtg_name;
102{
103     char str[256];
104     int result;
105     int mtg_name_len;
106     trn_base tb;
107     int uid = (int)geteuid();
108     struct stat sb;
109
110     mtg_name_len = strlen (mtg_name);
111     if (mtg_name[0] != '/' || mtg_name_len == 0 || mtg_name_len > 168
112         || mtg_name [mtg_name_len-1] == '/') {
113          return (BAD_PATH);
114     }
115
116     /* Check for moved meeting */
117     (void) strcpy (str, mtg_name);
118     (void) strcat (str, "/forward");
119     if (!stat(str, &sb)) {
120          return(MTG_MOVED);
121     }
122
123     (void) strcpy (str, mtg_name);
124     (void) strcat (str, "/acl");
125
126     if (!strcmp (mtg_name, current_mtg)) {
127          /*
128           * is acl stale?
129           */
130          if (stat(str, &sb) >= 0)
131               if (sb.st_mtime <= last_acl_mod)
132                    return (0);                         /* that was easy */
133     }
134
135     if (current_mtg [0] != '\0') {             /* close previous meeting */
136          if (nuclear)
137               panic ("Nuclear flag error");    /* should never happen */
138          (void) close (u_trn_f);
139          (void) close (u_control_f);
140          current_mtg [0] = '\0';
141          acl_destroy(mtg_acl);
142     }
143
144     u_trn_f = u_control_f = u_acl_f = 0;
145
146     if ((u_acl_f = open(str, O_RDONLY, 0700)) < 0) {
147          if (errno == ENOENT)
148               result = NO_SUCH_MTG;
149          else if (errno == EACCES)
150               result = NO_ACCESS;
151          else
152               result = BAD_PATH;
153          goto punt;
154     }
155     if (!fis_owner (u_acl_f, uid)) {
156          result = NO_ACCESS;
157          goto punt;
158     }
159
160     mtg_acl = acl_read (u_acl_f);
161     (void) fstat(u_acl_f, &sb);
162     last_acl_mod = sb.st_mtime;
163     (void) close(u_acl_f);
164     u_acl_f = 0;
165
166     (void) strcpy (str, mtg_name);
167     (void) strcat (str, "/control");
168
169     if ((u_control_f = open(str, O_RDWR, 0700)) < 0) {
170          if (errno == ENOENT)
171               result = NO_SUCH_MTG;
172          else if (errno == EACCES)
173               result = NO_ACCESS;
174          else
175               result = BAD_PATH;
176          goto punt;
177     }
178
179     if (!fis_owner (u_control_f, uid)) {
180          result = NO_ACCESS;
181          goto punt;
182     }
183
184     (void) strcpy (str, mtg_name);
185     (void) strcat (str, "/transactions");
186     
187     if ((u_trn_f = open(str, O_RDWR, 0700)) < 0) {
188          if (errno == ENOENT)
189               result = NO_SUCH_MTG;
190          else if (errno == EACCES)
191               result = NO_ACCESS;
192          else
193               result = BAD_PATH;
194          goto punt;
195     }
196
197     if (!fis_owner (u_trn_f, uid)) {
198          result = NO_ACCESS;
199          goto punt;
200     }
201
202     read (u_trn_f, (char *) &tb, sizeof (trn_base));
203     if (tb.unique == TRN_BASE_UNIQUE_SWAP) {
204          swap_trn_base(&tb);
205          mtg_swapped = TRUE;
206     } else {
207          mtg_swapped = FALSE;
208     }
209
210     if (tb.unique != TRN_BASE_UNIQUE) {
211          result = INCONSISTENT;
212          goto punt;
213     }
214
215     (void) strcpy (current_mtg, mtg_name);
216
217     return (0);
218
219 punt:
220     if (u_trn_f)
221             (void) close(u_trn_f);
222     if (u_control_f)
223             (void) close(u_control_f);
224     if (u_acl_f)
225             (void) close(u_acl_f);
226     acl_destroy(mtg_acl);
227     mtg_acl = NULL;
228     return (result);
229}
230
231
232int read_super()
233{
234     if (nuclear) {
235          aread (a_control_f, (char *) &super, sizeof(super), 0);
236     } else {
237             lseek (u_control_f, (long)0, SEEK_SET);
238             read (u_control_f, (char *) &super, sizeof (super));
239     }
240
241     if (!mtg_swapped) {
242          if (super.unique != MTG_SUPER_UNIQUE)
243               return (INCONSISTENT);
244     } else {
245          if (super.unique != MTG_SUPER_UNIQUE_SWAP)
246               return (INCONSISTENT);
247          swap_super(&super);
248     }
249
250     if (super.version != MTG_SUPER_1)
251          return (NEW_VERSION);
252
253     super_long_name = malloc ((unsigned)super.long_name_len);
254     super_chairman = malloc ((unsigned)super.chairman_len);
255
256     if (nuclear) {
257          aread (a_control_f, super_long_name, super.long_name_len, super.long_name_addr);
258          aread (a_control_f, super_chairman, super.chairman_len, super.chairman_addr);
259     } else {
260          lseek (u_control_f, (long)super.long_name_addr, SEEK_SET);
261          read (u_control_f, super_long_name, super.long_name_len);
262          lseek (u_control_f, (long)super.chairman_addr, SEEK_SET);
263          read (u_control_f, super_chairman, super.chairman_len);
264     }
265
266     return(0);
267}
268
269write_super ()
270{
271     int sc_len, sl_len;
272     int slong_name_len, slong_name_addr;
273     int schairman_len, schairman_addr;
274
275     sc_len = strlen (super_chairman)+1;
276     sl_len = strlen (super_long_name)+1;
277
278     if (sc_len != super.chairman_len || sl_len != super.long_name_len) {               /* reallocate things */
279          super.long_name_addr = sizeof (super);
280          super.long_name_len = sl_len;
281          super.chairman_addr = super.long_name_addr + super.long_name_len;
282          super.chairman_len = sc_len;
283     }
284
285     /* Copy into locals to avoid swapping */
286     slong_name_len = super.long_name_len;
287     slong_name_addr = super.long_name_addr;
288     schairman_len = super.chairman_len;
289     schairman_addr = super.chairman_addr;
290
291     if (mtg_swapped)
292          swap_super(&super);
293
294     if (nuclear) {
295          awrite (a_control_f, (char *) &super, sizeof (super), 0);
296          awrite (a_control_f, super_long_name, slong_name_len, slong_name_addr);
297          awrite (a_control_f, super_chairman, schairman_len, schairman_addr);
298     } else {
299          lseek (u_control_f, (long)0, SEEK_SET);
300          write (u_control_f, (char *)  &super, sizeof (super));
301          lseek (u_control_f, (long)slong_name_addr, SEEK_SET);
302          write (u_control_f, super_long_name, slong_name_len);
303          lseek (u_control_f, (long)schairman_addr, SEEK_SET);
304          write (u_control_f, super_chairman, schairman_len);
305     }
306
307     free (super_chairman);
308     super_chairman = NULL;
309     free (super_long_name);
310     super_long_name = NULL;
311     super.unique = 0;                                  /* prevent accidents */
312
313     return;
314}
315
316/*
317 *
318 * forget_super () -- Routine to dump superblock, like when we just read
319 *                    it.  This frees any alloc'd storage.
320 *
321 */
322forget_super()
323{
324     if (super_long_name != NULL)
325          free(super_long_name);
326     super_long_name = NULL;
327
328     if (super_chairman != NULL)
329          free(super_chairman);
330     super_chairman = NULL;
331     super.unique = 0;
332}
333
334/*
335 *
336 * start_read () --     This reserves the meeting for non-destructive reading.
337 *                      Simply does an flock.
338 *
339 */
340start_read()
341{
342     struct flock lock;
343
344     if (!no_nuke) {
345          lock.l_type = F_RDLCK;
346          lock.l_start = 0;
347          lock.l_whence = 0;
348          lock.l_len = 0;
349          if (fcntl(u_control_f, F_SETLKW, &lock) < 0)
350               panic ("Cannot share lock");
351          read_lock = TRUE;
352     }
353}
354
355/*
356 *
357 * finish_read () --    This frees up our reservation on the meeting.  This
358 *                      does the opposite of start_read.
359 *
360 */
361finish_read()
362{
363    struct flock lock;
364
365     if (!no_nuke) {
366           lock.l_type = F_UNLCK;
367          lock.l_start = 0;
368          lock.l_whence = 0;
369          lock.l_len = 0;
370          fcntl(u_control_f, F_SETLK, &lock);
371          read_lock = 0;
372     }
373}
374
375/*
376 *
377 * chain_addr -- Returns address of given chain block.
378 *
379 */
380faddr chain_addr(trn)
381trn_nums trn;
382{
383     if (trn < super.lowest || trn > super.highest)
384          return (0);
385
386     return (super.chain_start + (trn-1) * sizeof (chain_blk));
387}
388
389int read_chain (trn, cb)
390trn_nums trn;
391chain_blk *cb;
392{
393     faddr cbaddr;
394
395     cbaddr = chain_addr (trn);
396     if (cbaddr == 0)
397          return (NO_SUCH_TRN);
398
399     if (nuclear)
400          aread (a_control_f, (char *) cb, sizeof (chain_blk), cbaddr);
401     else {
402          lseek (u_control_f, (long)cbaddr, SEEK_SET);
403          read (u_control_f, (char *) cb, sizeof (chain_blk));
404     }
405
406     if (mtg_swapped)
407          swap_chain(cb);
408
409     if (cb -> unique != CHAIN_BLK_UNIQUE || cb -> current != trn)
410          return (INCONSISTENT);
411
412     if (cb -> version != CHAIN_BLK_1)
413          return (NEW_VERSION);
414
415     return (0);
416}
417
418int write_chain (cb)
419chain_blk *cb;
420{
421     faddr cbaddr;
422
423     cbaddr = chain_addr (cb -> current);
424     if (cbaddr == 0)
425          return (NO_SUCH_TRN);
426
427     if (mtg_swapped)
428          swap_chain(cb);
429
430     if (nuclear) {
431          if (awrite (a_control_f, (char *) cb, sizeof (chain_blk), cbaddr) != sizeof (chain_blk)) {
432               if (mtg_swapped)
433                    swap_chain(cb);
434               return (NO_WRITE);
435          }
436     }
437     else {
438          lseek (u_control_f, (long)cbaddr, SEEK_SET);
439          if (write (u_control_f, (char *) cb, sizeof (chain_blk)) != sizeof (chain_blk)) {
440               if (mtg_swapped)
441                    swap_chain(cb);
442               return (NO_WRITE);
443          }
444     }
445
446     if (mtg_swapped)
447          swap_chain(cb);
448     return (0);
449}
450
451/*
452 *
453 * read_trn -- routine to read a transaction from the transaction file.
454 *
455 */
456int read_trn (trn_addr, th, th_subject, th_author, th_signature)
457faddr trn_addr;
458trn_hdr *th;
459char **th_subject, **th_author, **th_signature;
460{
461     char *author;
462
463     lseek (u_trn_f, (long)trn_addr, SEEK_SET);
464     read (u_trn_f, (char *) th, sizeof (trn_hdr));
465
466     if (mtg_swapped)
467          swap_trn(th);
468
469     if (th -> unique != TRN_HDR_UNIQUE)
470          return (INCONSISTENT);
471
472     if (th -> version != TRN_HDR_1)
473          return(NEW_VERSION);
474
475     if (th_subject != NULL) {
476          *th_subject = malloc ((unsigned)(th -> subject_len));
477          lseek (u_trn_f, (long)(th -> subject_addr), SEEK_SET);
478          read (u_trn_f, *th_subject, th -> subject_len);
479     }
480         
481     if (th_author != NULL || th_signature != NULL) {
482          author = malloc ((unsigned)(th -> author_len));
483          lseek (u_trn_f, (long)(th -> author_addr), SEEK_SET);
484          read (u_trn_f, author, th -> author_len);
485     }
486
487     if (th_author != NULL)
488          *th_author = author;
489
490     if (th_signature != NULL) {
491          if (strlen(author)+1 == th -> author_len) {
492               *th_signature = new_string(author);      /* No signature, return author */
493          } else {
494               *th_signature = new_string(&author[strlen(author)+1]);
495          }
496          if (th_author == NULL)
497               free(author);
498     }
499               
500     return(0);
501}
502         
503
504/*
505 *
506 * core_abort () -- Routine for use by core routines to clean things up.
507 *
508 */
509core_abort ()
510{
511     int dummy;
512
513     if (nuclear) {
514          aabort(a_control_f);
515          nuclear = FALSE;
516     }
517
518     if (abort_file != NULL) {
519          tclose (abort_file,&dummy);
520          abort_file = NULL;
521     }
522
523     if (read_lock)
524          finish_read();
525
526     forget_super();
527
528     return;
529}
530
531/*
532 *
533 * fsize () -- Routine to find out the size of a file.
534 *
535 */
536fsize (d)
537int d;
538{
539     struct stat buf;
540
541     if (fstat (d, &buf) < 0)
542          return (0);
543
544     return (buf.st_size);
545}
546/*
547 *
548 * fis_owner () -- Routine to find out if uid is owner of file.
549 *
550 */
551fis_owner (d, uid)
552int d,uid;
553{
554     struct stat buf;
555
556     if (fstat (d, &buf) < 0)
557          return (0);
558
559     return (uid == buf.st_uid);
560}
561
562/*
563 *
564 * has_trn_access -- Routine to return true if current user can access
565 *                   transaction.
566 *
567 */
568bool has_trn_access(author,mode)
569char *author;
570char mode;
571{
572     char *test_modes;
573
574     if (has_privs)
575          return(TRUE);                         /* linked in */
576
577     switch (mode) {
578     case 'd':
579          if (!strcmp(author,rpc_caller)) {
580               test_modes = "o";
581          } else {
582               test_modes = "d";
583          }
584          return (acl_check(mtg_acl,rpc_caller,test_modes));
585
586     case 'r':
587          return (acl_check(mtg_acl,rpc_caller,"r") ||
588                  ((!strcmp (author, rpc_caller)) && acl_check(mtg_acl,rpc_caller,"o")));
589     default:
590          panic ("Invalid mode");
591          /*NOTREACHED*/
592     }
593}         
594/*
595 *
596 * has_mtg_access -- Routine to return true if current user can access
597 *                   mtg, with given mode.
598 *
599 */
600bool has_mtg_access(mode)
601char mode;
602{
603     char *test_modes;
604     char mode_str[3];
605
606     if (has_privs)                             /* linked in stuff */
607          return (TRUE);               
608
609     switch (mode) {
610     case 'w':
611     case 'a':
612     case 'r':
613     case 's':
614     case 'o':
615          mode_str[0] = mode;
616          mode_str[1] = '\0';
617          test_modes = mode_str;
618          break;
619
620     case 'c':
621          test_modes = "c";
622          break;
623
624     default:
625          panic("Invalid mode");
626     }
627
628     return(acl_check(mtg_acl,rpc_caller,test_modes));
629}         
630/*
631 *
632 * panic -- just a printf
633 *
634 */
635panic(str)
636char *str;
637{
638     printf("panic: %s\n",str);
639     perror("discuss");
640     exit(1);
641}
642
643#if HAVE_ZEPHYR
644/*
645 *
646 * mtg_znotify -- send off a Zephyr notification as appropriate
647 *
648 */
649
650static const char * this_host = (const char *) NULL;
651
652void mtg_znotify(mtg_name, subject, author, signature)
653        char *mtg_name, *subject, *author, *signature;
654{
655        register dsc_acl_entry *ae;
656        register int n;
657        ZNotice_t notice;
658        char *msglst[5],bfr[30],fullpath[256];
659        int code, list_size;
660
661        if (!use_zephyr)
662            return;
663
664        if (!this_host) {
665            /* perform initializations */
666            char *h;
667            char host[100];
668            struct hostent *hent;
669            if (gethostname(host,100) != 0)
670                return;
671            hent = (struct hostent *) gethostbyname(host);
672            if (hent == 0)
673                return;
674            h = (char *) malloc (strlen (hent->h_name) + 1);
675            if (!h)
676                return;
677            strcpy (h, hent->h_name);
678            this_host = h;
679            ZInitialize();
680        }
681
682        /* Set up the notice structure */
683        memset(&notice, 0, sizeof(notice));
684
685        sprintf(fullpath,"%s:%s", this_host, mtg_name);
686        ZOpenPort(NULL);
687        notice.z_kind = UNSAFE;
688        notice.z_port = 0;
689        notice.z_class = "DISCUSS";
690        notice.z_class_inst = fullpath;
691        notice.z_opcode = "NEW_TRN";
692        notice.z_sender = 0;
693        if (signature == NULL)
694             notice.z_default_format = "New transaction [$1] entered in $2\nFrom: $3\nSubject: $4";
695        else
696             notice.z_default_format = "New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4";
697        msglst[0] = bfr;
698        sprintf(msglst[0],"%04d",super.highest);
699        msglst[1] = super_long_name;
700        msglst[2] = author;
701        msglst[3] = subject;
702        list_size = 4;
703        if (signature != NULL) {
704             msglst[4] = signature;
705             list_size = 5;
706        }
707
708        /* Does "*" have read access? If so, just send out a global
709         * notice.
710         */
711
712        /* XXX
713         * Check at some point for people who don't have access, etc.
714         */
715
716        for (ae = mtg_acl->acl_entries, n=mtg_acl->acl_length;
717             n;
718             ae++, n--) {
719                if ((strcmp("*", ae->principal) == 0) &&
720                    acl_is_subset("r", ae->modes))
721                        break;
722        }
723        if (n) {
724                notice.z_recipient = "";
725                /* We really don't care if it gets through... */
726                code = ZSendList(&notice,msglst,list_size,ZNOAUTH);
727                return;
728        }
729        for (ae = mtg_acl->acl_entries, n=mtg_acl->acl_length;
730             n;
731             ae++, n--) {
732                if (acl_is_subset("r", ae->modes)) {
733                        notice.z_recipient = ae->principal;
734                        ZSendList(&notice,msglst,list_size,ZNOAUTH);
735                }
736        }
737}
738#endif
739
740static unsigned long swap_32(val)
741unsigned long val;
742{
743     unsigned char b1 = (val >> 24) & 0xff;
744     unsigned char b2 = (val >> 16) & 0xff;
745     unsigned char b3 = (val >> 8) & 0xff;
746     unsigned char b4 = val & 0xff;
747
748     return ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
749}
750
751static unsigned int swap_16(val)
752unsigned int val;
753{
754     unsigned char b1 = (val >> 8) & 0xff;
755     unsigned char b2 = val & 0xff;
756
757     return ((b2 << 8) | b1);
758}
759
760swap_super(superp)
761mtg_super *superp;
762{
763     superp->version = swap_32(superp->version);
764     superp->unique = swap_32(superp->unique);
765     superp->first = swap_32(superp->first);
766     superp->last = swap_32(superp->last);
767     superp->lowest = swap_32(superp->lowest);
768     superp->highest = swap_32(superp->highest);
769     superp->highest_chain = swap_32(superp->highest_chain);
770     superp->date_created = swap_32(superp->date_created);
771     superp->date_modified = swap_32(superp->date_modified);
772     superp->long_name_addr = swap_32(superp->long_name_addr);
773     superp->chairman_addr = swap_32(superp->chairman_addr);
774     superp->long_name_len = swap_16(superp->long_name_len);
775     superp->chairman_len = swap_16(superp->chairman_len);
776     superp->flags = swap_16(superp->flags);
777     superp->chain_start = swap_32(superp->chain_start);
778     superp->high_water = swap_32(superp->high_water);
779     superp->trn_fsize = swap_32(superp->trn_fsize);
780     superp->highest_trn_addr = swap_32(superp->highest_trn_addr);
781}
782
783swap_chain(cbp)
784chain_blk *cbp;
785{
786     cbp->version = swap_32(cbp->version);
787     cbp->unique = swap_32(cbp->unique);
788     cbp->current = swap_32(cbp->current);
789     cbp->prev = swap_32(cbp->prev);
790     cbp->next = swap_32(cbp->next);
791     cbp->pref = swap_32(cbp->pref);
792     cbp->nref = swap_32(cbp->nref);
793     cbp->trn_chain = swap_32(cbp->trn_chain);
794     cbp->trn_addr = swap_32(cbp->trn_addr);
795     cbp->flags = swap_16(cbp->flags);
796     cbp->filler = swap_16(cbp->filler);
797     cbp->chain_fref = swap_32(cbp->chain_fref);
798     cbp->chain_lref = swap_32(cbp->chain_lref);
799}
800
801swap_trn_base(tbp)
802trn_base *tbp;
803{
804     tbp->version = swap_32(tbp->version);
805     tbp->unique = swap_32(tbp->unique);
806     tbp->date_created = swap_32(tbp->date_created);
807     tbp->long_name_addr = swap_32(tbp->long_name_addr);
808     tbp->chairman_addr = swap_32(tbp->chairman_addr);
809     tbp->long_name_len = swap_16(tbp->long_name_len);
810     tbp->chairman_len = swap_16(tbp->chairman_len);
811     tbp->public_flag = swap_16(tbp->public_flag);
812}
813
814swap_trn(thp)
815trn_hdr *thp;
816{
817     thp->version = swap_32(thp->version);
818     thp->unique = swap_32(thp->unique);
819     thp->current = swap_32(thp->current);
820     thp->orig_pref = swap_32(thp->orig_pref);
821     thp->date_entered = swap_32(thp->date_entered);
822     thp->num_lines = swap_32(thp->num_lines);
823     thp->num_chars = swap_32(thp->num_chars);
824     thp->prev_trn = swap_32(thp->prev_trn);
825     thp->subject_addr = swap_32(thp->subject_addr);
826     thp->author_addr = swap_32(thp->author_addr);
827     thp->text_addr = swap_32(thp->text_addr);
828     thp->subject_len = swap_16(thp->subject_len);
829     thp->author_len = swap_16(thp->author_len);
830}
Note: See TracBrowser for help on using the repository browser.