blob: b9f9dea126bc6e502c681264088717abc9ebb5ef [file] [log] [blame]
Theodore Ts'o21c84b71997-04-29 16:15:03 +00001/*
2 * message.c --- print e2fsck messages (with compression)
3 *
4 * Copyright 1996, 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * print_e2fsck_message() prints a message to the user, using
12 * compression techniques and expansions of abbreviations.
13 *
14 * The following % expansions are supported:
15 *
16 * %b <blk> block number
17 * %B <blkcount> integer
18 * %di <dirent>->ino inode number
19 * %dn <dirent>->name string
20 * %D <dir> inode number
21 * %g <group> integer
22 * %i <ino> inode number
23 * %Is <inode> -> i_size
24 * %Ib <inode> -> i_blocks
25 * %Il <inode> -> i_links_count
26 * %Im <inode> -> i_mode
27 * %IM <inode> -> i_mtime
28 * %IF <inode> -> i_faddr
29 * %If <inode> -> i_file_acl
30 * %Id <inode> -> i_dir_acl
31 * %j <ino2> inode number
32 * %N <num>
33 * %p ext2fs_get_pathname of directory <ino>
34 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
35 * the containing directory. (If dirent is NULL
36 * then return the pathname of directory <ino2>)
37 * %q ext2fs_get_pathname of directory <dir>
38 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
39 * the containing directory.
40 *
41 * The following '@' expansions are supported:
42 *
43 * @b block
44 * @B bitmap
45 * @C conflicts with some other fs block
46 * @i inode
Theodore Ts'o7cf73dc1997-08-14 17:17:16 +000047 * @I illegal
Theodore Ts'o21c84b71997-04-29 16:15:03 +000048 * @D deleted
49 * @d directory
50 * @e entry
51 * @E Entry '%Dn' in %p (%i)
52 * @F for @i %i (%Q) is
53 * @g group
54 * @l lost+found
55 * @L is a link
56 * @u unattached
57 * @r root inode
58 * @z zero-length
59 */
60
61#include <stdlib.h>
62#include <unistd.h>
63#include <string.h>
64#include <ctype.h>
65#include <termios.h>
66
67#include "e2fsck.h"
68
69#include "problem.h"
70
71#ifdef __GNUC__
72#define _INLINE_ __inline__
73#else
74#define _INLINE_
75#endif
76
77/*
78 * This structure defines the abbreviations used by the text strings
79 * below. The first character in the string is the index letter. An
80 * abbreviation of the form '@<i>' is expanded by looking up the index
81 * letter <i> in the table below.
82 */
83static const char *abbrevs[] = {
84 "bblock",
85 "Bbitmap",
86 "Cconflicts with some other fs @b",
87 "iinode",
Theodore Ts'o7cf73dc1997-08-14 17:17:16 +000088 "Iillegal",
Theodore Ts'o21c84b71997-04-29 16:15:03 +000089 "Ddeleted",
90 "ddirectory",
91 "eentry",
92 "E@e '%Dn' in %p (%i)",
93 "Ffor @i %i (%Q) is",
94 "ggroup",
95 "llost+found",
96 "Lis a link",
97 "uunattached",
98 "rroot @i",
99 "sshould be",
100 "zzero-length",
101 "@@",
102 0
103 };
104
105/*
106 * Give more user friendly names to the "special" inodes.
107 */
108#define num_special_inodes 7
109static const char *special_inode_name[] =
110{
111 "<The NULL inode>", /* 0 */
112 "<The bad blocks inode>", /* 1 */
113 "/", /* 2 */
114 "<The ACL index inode>", /* 3 */
115 "<The ACL data inode>", /* 4 */
116 "<The boot loader inode>", /* 5 */
117 "<The undelete directory inode>" /* 6 */
118};
119
120/*
121 * This function prints a pathname, using the ext2fs_get_pathname
122 * function
123 */
124static void print_pathname(ext2_filsys fs, ino_t dir, ino_t ino)
125{
126 errcode_t retval;
127 char *path;
128
129 if (!dir && (ino < num_special_inodes)) {
130 fputs(special_inode_name[ino], stdout);
131 return;
132 }
133
134 retval = ext2fs_get_pathname(fs, dir, ino, &path);
135 if (retval)
136 fputs("???", stdout);
137 else {
138 fputs(path, stdout);
139 free(path);
140 }
141}
142
143/*
144 * This function handles the '@' expansion. We allow recursive
145 * expansion; an @ expression can contain further '@' and '%'
146 * expressions.
147 */
148static _INLINE_ void expand_at_expression(ext2_filsys fs, char ch,
149 struct problem_context *ctx,
150 int *first)
151{
152 const char **cpp, *str;
153
154 /* Search for the abbreviation */
155 for (cpp = abbrevs; *cpp; cpp++) {
156 if (ch == *cpp[0])
157 break;
158 }
159 if (*cpp) {
160 str = (*cpp) + 1;
161 if (*first && islower(*str)) {
162 *first = 0;
163 fputc(toupper(*str++), stdout);
164 }
165 print_e2fsck_message(fs, str, ctx, *first);
166 } else
167 printf("@%c", ch);
168}
169
170/*
171 * This function expands '%kX' expressions
172 */
173static _INLINE_ void expand_inode_expression(char ch,
174 struct problem_context *ctx)
175{
176 struct ext2_inode *inode;
177 char * time_str;
178 time_t t;
179
180 if (!ctx || !ctx->inode)
181 goto no_inode;
182
183 inode = ctx->inode;
184
185 switch (ch) {
186 case 's':
187 printf("%u", inode->i_size);
188 break;
189 case 'b':
190 printf("%u", inode->i_blocks);
191 break;
192 case 'l':
193 printf("%d", inode->i_links_count);
194 break;
195 case 'm':
196 printf("0%o", inode->i_mode);
197 break;
198 case 'M':
199 t = inode->i_mtime;
200 time_str = ctime(&t);
201 printf("%.24s", time_str);
202 break;
203 case 'F':
204 printf("%u", inode->i_faddr);
205 break;
206 case 'f':
207 printf("%u", inode->i_file_acl);
208 break;
209 case 'd':
210 printf("%u", inode->i_dir_acl);
211 break;
212 default:
213 no_inode:
214 printf("%%I%c", ch);
215 break;
216 }
217}
218
219/*
220 * This function expands '%dX' expressions
221 */
222static _INLINE_ void expand_dirent_expression(char ch,
223 struct problem_context *ctx)
224{
225 struct ext2_dir_entry *dirent;
226 int len;
227
228 if (!ctx || !ctx->dirent)
229 goto no_dirent;
230
231 dirent = ctx->dirent;
232
233 switch (ch) {
234 case 'i':
235 printf("%u", dirent->inode);
236 break;
237 case 'n':
238 len = dirent->name_len;
239 if (len > EXT2_NAME_LEN)
240 len = EXT2_NAME_LEN;
241 if (len > dirent->rec_len)
242 len = dirent->rec_len;
243 printf("%.*s", dirent->name_len, dirent->name);
244 break;
245 default:
246 no_dirent:
247 printf("%%D%c", ch);
248 break;
249 }
250}
251
252static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
253 struct problem_context *ctx)
254{
255 if (!ctx)
256 goto no_context;
257
258 switch (ch) {
259 case '%':
260 fputc('%', stdout);
261 break;
262 case 'b':
263 printf("%u", ctx->blk);
264 break;
265 case 'B':
266 printf("%d", ctx->blkcount);
267 break;
268 case 'd':
269 printf("%lu", ctx->dir);
270 break;
271 case 'g':
272 printf("%d", ctx->group);
273 break;
274 case 'i':
275 printf("%lu", ctx->ino);
276 break;
277 case 'j':
278 printf("%lu", ctx->ino2);
279 break;
280 case 'N':
281 printf("%u", ctx->num);
282 break;
283 case 'p':
284 print_pathname(fs, ctx->ino, 0);
285 break;
286 case 'P':
287 print_pathname(fs, ctx->ino2,
288 ctx->dirent ? ctx->dirent->inode : 0);
289 break;
290 case 'q':
291 print_pathname(fs, ctx->dir, 0);
292 break;
293 case 'Q':
294 print_pathname(fs, ctx->dir, ctx->ino);
295 break;
296 default:
297 no_context:
298 printf("%%%c", ch);
299 break;
300 }
301}
302
303void print_e2fsck_message(ext2_filsys fs, const char *msg,
304 struct problem_context *ctx, int first)
305{
306 const char * cp;
307 int i;
308
309 for (cp = msg; *cp; cp++) {
310 if (cp[0] == '@') {
311 cp++;
312 expand_at_expression(fs, *cp, ctx, &first);
313 } else if (cp[0] == '%' && cp[1] == 'I') {
314 cp += 2;
315 expand_inode_expression(*cp, ctx);
316 } else if (cp[0] == '%' && cp[1] == 'D') {
317 cp += 2;
318 expand_dirent_expression(*cp, ctx);
319 } else if ((cp[0] == '%')) {
320 cp++;
321 expand_percent_expression(fs, *cp, ctx);
322 } else {
323 for (i=0; cp[i]; i++)
324 if ((cp[i] == '@') || cp[i] == '%')
325 break;
326 printf("%.*s", i, cp);
327 cp += i-1;
328 }
329 first = 0;
330 }
331}