about summary refs log tree commit diff
path: root/lib/fileio.c
blob: d95e5f8c8dd67477d3bd66192bfb56cb7f74232e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/* fileio.c - routines to read elements from Netpbm image files
**
** Copyright (C) 1988 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include <stdio.h>
#include <limits.h>

#include "pm.h"
#include "fileio.h"

char
pm_getc(FILE * const fileP) {
    int ich;
    char ch;

    ich = getc(fileP);
    if (ich == EOF)
        pm_error("EOF / read error reading a byte");
    ch = (char) ich;

    if (ch == '#') {
        do {
            ich = getc(fileP);
            if (ich == EOF)
                pm_error("EOF / read error reading a byte");
            ch = (char) ich;
        } while (ch != '\n' && ch != '\r');
    }
    return ch;
}



/* This is useful for PBM files.  It used to be used for PGM and PPM files
   too, since the sample size was always one byte.  Now, use pbm_getrawsample()
   for PGM and PPM files.
*/

unsigned char
pm_getrawbyte(FILE * const file) {
    int iby;

    iby = getc(file);
    if (iby == EOF)
        pm_error("EOF / read error reading a one-byte sample");
    return (unsigned char) iby;
}



unsigned int
pm_getuint(FILE * const ifP) {
/*----------------------------------------------------------------------------
   Read an unsigned integer in ASCII decimal from the file stream
   represented by 'ifP' and return its value.

   If there is nothing at the current position in the file stream that
   can be interpreted as an unsigned integer, issue an error message
   to stderr and abort the program.

   If the number at the current position in the file stream is too
   great to be represented by an 'int' (Yes, I said 'int', not
   'unsigned int'), issue an error message to stderr and abort the
   program.
-----------------------------------------------------------------------------*/
    char ch;
    unsigned int i;

    do {
        ch = pm_getc(ifP);
    } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');

    if (ch < '0' || ch > '9')
        pm_error("junk in file where an unsigned integer should be");

    i = 0;
    do {
        unsigned int const digitVal = ch - '0';

        if (i > INT_MAX/10)
            pm_error("ASCII decimal integer in file is "
                     "too large to be processed.  ");

        i *= 10;

        if (i > INT_MAX - digitVal)
            pm_error("ASCII decimal integer in file is "
                     "too large to be processed.  ");

        i += digitVal;

        ch = pm_getc(ifP);
    } while (ch >= '0' && ch <= '9');

    return i;
}



unsigned int
pm_getraw(FILE *       const file,
          unsigned int const bytes) {

    unsigned int value;  /* our return value */

    if (bytes == 1) {
        /* Here's a speedup for the common 1-byte sample case: */
        value = getc(file);
        if (value == EOF)
            pm_error("EOF/error reading 1 byte sample from file.");
    } else {
        /* This code works for bytes == 1..4 */
        /* We could speed this up by exploiting knowledge of the format of
           an unsigned integer (i.e. endianness).  Then we could just cast
           the value as an array of characters instead of shifting and
           masking.
           */
        int shift;
        unsigned char inval[4];
        int cursor;
        int n_read;

        n_read = fread(inval, bytes, 1, file);
        if (n_read < 1)
            pm_error("EOF/error reading %d byte sample from file.", bytes);
        value = 0;  /* initial value */
        cursor = 0;
        for (shift = (bytes-1)*8; shift >= 0; shift-=8)
            value += inval[cursor++] << shift;
    }
    return(value);
}



void
pm_putraw(FILE *       const file,
          unsigned int const value,
          unsigned int const bytes) {

    if (bytes == 1) {
        /* Here's a speedup for the common 1-byte sample case: */
        int rc;
        rc = fputc(value, file);
        if (rc == EOF)
            pm_error("Error writing 1 byte sample to file.");
    } else {
        /* This code works for bytes == 1..4 */
        /* We could speed this up by exploiting knowledge of the format of
           an unsigned integer (i.e. endianness).  Then we could just cast
           the value as an array of characters instead of shifting and
           masking.
           */
        int shift;
        unsigned char outval[4];
        int cursor;
        int n_written;

        cursor = 0;
        for (shift = (bytes-1)*8; shift >= 0; shift-=8) {
            outval[cursor++] = (value >> shift) & 0xFF;
        }
        n_written = fwrite(&outval, bytes, 1, file);
        if (n_written == 0)
            pm_error("Error writing %d byte sample to file.", bytes);
    }
}