1#!/usr/bin/env slsh
2
3% This script prints the width and height of one or more jpeg image files.
4if (__argc < 2)
5{
6   () = fprintf (stderr, "Usage: %s files....\n", path_basename (__argv[0]));
7   exit (1);
8}
9
10private variable M_SOF0	= 0xC0;	       %/* Start Of Frame N */
11private variable M_SOF1	= 0xC1;	       %/* N indicates which compression process */
12private variable M_SOF2	= 0xC2;	       %/* Only SOF0-SOF2 are now in common use */
13private variable M_SOF3	= 0xC3;	       %
14private variable M_SOF5	= 0xC5;	       %/* NB: codes C4 and CC are NOT SOF markers */
15private variable M_SOF6	= 0xC6;	       %
16private variable M_SOF7	= 0xC7;	       %
17private variable M_SOF9	= 0xC9;	       %
18private variable M_SOF10	= 0xCA;	       %
19private variable M_SOF11	= 0xCB;	       %
20private variable M_SOF13	= 0xCD;	       %
21private variable M_SOF14	= 0xCE;	       %
22private variable M_SOF15	= 0xCF;	       %
23private variable M_SOI	= 0xD8;	       %/* Start Of Image (beginning of datastream) */
24private variable M_EOI	= 0xD9;	       %/* End Of Image (end of datastream) */
25private variable M_SOI	= 0xD8;	       % Start Of Image (beginning of datastream)
26
27private define read_nbytes (fp, n)
28{
29   variable b;
30   if (n != fread_bytes (&b, n, fp))
31     throw ReadError, "Failed to read $n bytes"$;
32   return b;
33}
34
35private define read_ushort (fp)
36{
37   return unpack (">H", read_nbytes (fp, 2));
38}
39
40private define open_jpg_file_for_read (file)
41{
42   variable fp = fopen (file, "rb");
43   variable b = read_nbytes (fp, 2);
44   if ((b[0] != 0xFF) or (b[1] != M_SOI))
45     return NULL;
46   return fp;
47}
48
49private define next_marker (fp)
50{
51   variable c = read_nbytes (fp, 1);
52   while (c != 0xFF)
53     c = read_nbytes (fp, 1);
54
55   % Remove pad bytes
56   do
57     c = read_nbytes (fp, 1);
58   while (c == 0xFF);
59
60   return c;
61}
62
63private define skip_variable (fp)
64{
65   variable len = read_ushort (fp);
66   if (len < 2)
67     throw DataError, "Erroneous JPEG marker length";
68   len -= 2;
69
70   while (len > 512)
71     {
72	() = read_nbytes (fp, 512);
73	len -= 512;
74     }
75   () = read_nbytes (fp, len);
76}
77
78public define jpeg_size (file, width, height)
79{
80   variable fp = open_jpg_file_for_read (file);
81   if (fp == NULL)
82     return -1;
83
84   variable is_sof = UChar_Type[256];
85   is_sof[M_SOF0] = 1;
86   is_sof[M_SOF1] = 1;
87   is_sof[M_SOF2] = 1;
88   is_sof[M_SOF3] = 1;
89   is_sof[M_SOF5] = 1;
90   is_sof[M_SOF6] = 1;
91   is_sof[M_SOF7] = 1;
92   is_sof[M_SOF9] = 1;
93   is_sof[M_SOF10] = 1;
94   is_sof[M_SOF11] = 1;
95   is_sof[M_SOF13] = 1;
96   is_sof[M_SOF15] = 1;
97
98   while (0 == is_sof[next_marker(fp)])
99     {
100	skip_variable (fp);
101     }
102
103   variable len = read_ushort (fp);
104   variable data_precision = read_nbytes (fp, 1);
105   variable h = read_ushort (fp);
106   variable w = read_ushort (fp);
107
108   @width = w;
109   @height = h;
110   return 0;
111}
112
113private define main ()
114{
115   variable file;
116   foreach file (__argv[[1:]])
117     {
118	try
119	  {
120	     variable width, height;
121	     if (-1 == jpeg_size (file, &width, &height))
122	       {
123		  () = fprintf (stderr, "%s is not a jpeg file\n", file);
124		  continue;
125	       }
126	     () = fprintf (stdout, "%s: %ux%u\n", file, width, height);
127	  }
128	catch ReadError, DataError:
129	() = fprintf (stderr, "Caught error processing %s ...skipped\n", file);
130     }
131   exit (0);
132}
133main ();
134