icculus quake2 doxygen  1.0 dev
console.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 // console.c
21 
22 #include "client.h"
23 
25 
27 
28 
29 #define MAXCMDLINE 256
30 extern char key_lines[32][MAXCMDLINE];
31 extern int edit_line;
32 extern int key_linepos;
33 
34 
35 void DrawString (int x, int y, char *s)
36 {
37  while (*s)
38  {
39 #ifdef QMAX
40  re.DrawChar (x, y, *s, 1);
41 #else
42  re.DrawChar (x, y, *s);
43 #endif
44  x+=8;
45  s++;
46  }
47 }
48 
49 void DrawAltString (int x, int y, char *s)
50 {
51  while (*s)
52  {
53 #ifdef QMAX
54  re.DrawChar (x, y, *s ^ 0x80, 1);
55 #else
56  re.DrawChar (x, y, *s ^ 0x80);
57 #endif
58  x+=8;
59  s++;
60  }
61 }
62 
63 
64 void Key_ClearTyping (void)
65 {
66  key_lines[edit_line][1] = 0; // clear any typing
67  key_linepos = 1;
68 }
69 
70 /*
71 ================
72 Con_ToggleConsole_f
73 ================
74 */
76 {
77  SCR_EndLoadingPlaque (); // get rid of loading plaque
78 
79  if (cl.attractloop)
80  {
81  Cbuf_AddText ("killserver\n");
82  return;
83  }
84 
85  if (cls.state == ca_disconnected)
86  { // start the demo loop again
87  Cbuf_AddText ("d1\n");
88  return;
89  }
90 
91  Key_ClearTyping ();
92  Con_ClearNotify ();
93 
94  if (cls.key_dest == key_console)
95  {
96  M_ForceMenuOff ();
97  Cvar_Set ("paused", "0");
98  }
99  else
100  {
101  M_ForceMenuOff ();
103 
104  if (Cvar_VariableValue ("maxclients") == 1
105  && Com_ServerState ())
106  Cvar_Set ("paused", "1");
107  }
108 }
109 
110 /*
111 ================
112 Con_ToggleChat_f
113 ================
114 */
115 void Con_ToggleChat_f (void)
116 {
117  Key_ClearTyping ();
118 
119  if (cls.key_dest == key_console)
120  {
121  if (cls.state == ca_active)
122  {
123  M_ForceMenuOff ();
125  }
126  }
127  else
129 
130  Con_ClearNotify ();
131 }
132 
133 /*
134 ================
135 Con_Clear_f
136 ================
137 */
138 void Con_Clear_f (void)
139 {
140  memset (con.text, ' ', CON_TEXTSIZE);
141 }
142 
143 
144 /*
145 ================
146 Con_Dump_f
147 
148 Save the console contents out to a file
149 ================
150 */
151 void Con_Dump_f (void)
152 {
153  int l, x;
154  char *line;
155  FILE *f;
156  char buffer[1024];
157  char name[MAX_OSPATH];
158 
159  if (Cmd_Argc() != 2)
160  {
161  Com_Printf ("usage: condump <filename>\n");
162  return;
163  }
164 
165  Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
166 
167  Com_Printf ("Dumped console text to %s.\n", name);
169  f = fopen (name, "w");
170  if (!f)
171  {
172  Com_Printf ("ERROR: couldn't open.\n");
173  return;
174  }
175 
176  // skip empty lines
177  for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
178  {
179  line = con.text + (l%con.totallines)*con.linewidth;
180  for (x=0 ; x<con.linewidth ; x++)
181  if (line[x] != ' ')
182  break;
183  if (x != con.linewidth)
184  break;
185  }
186 
187  // write the remaining lines
188  buffer[con.linewidth] = 0;
189  for ( ; l <= con.current ; l++)
190  {
191  line = con.text + (l%con.totallines)*con.linewidth;
192  strncpy (buffer, line, con.linewidth);
193  for (x=con.linewidth-1 ; x>=0 ; x--)
194  {
195  if (buffer[x] == ' ')
196  buffer[x] = 0;
197  else
198  break;
199  }
200  for (x=0; buffer[x]; x++)
201  buffer[x] &= 0x7f;
202 
203  fprintf (f, "%s\n", buffer);
204  }
205 
206  fclose (f);
207 }
208 
209 
210 /*
211 ================
212 Con_ClearNotify
213 ================
214 */
215 void Con_ClearNotify (void)
216 {
217  int i;
218 
219  for (i=0 ; i<NUM_CON_TIMES ; i++)
220  con.times[i] = 0;
221 }
222 
223 
224 /*
225 ================
226 Con_MessageMode_f
227 ================
228 */
229 void Con_MessageMode_f (void)
230 {
231  chat_team = false;
233 }
234 
235 /*
236 ================
237 Con_MessageMode2_f
238 ================
239 */
241 {
242  chat_team = true;
244 }
245 
246 /*
247 ================
248 Con_CheckResize
249 
250 If the line width has changed, reformat the buffer.
251 ================
252 */
253 void Con_CheckResize (void)
254 {
255  int i, j, width, oldwidth, oldtotallines, numlines, numchars;
256  char tbuf[CON_TEXTSIZE];
257 
258  width = (viddef.width >> 3) - 2;
259 
260  if (width == con.linewidth)
261  return;
262 
263  if (width < 1) // video hasn't been initialized yet
264  {
265  width = 38;
266  con.linewidth = width;
268  memset (con.text, ' ', CON_TEXTSIZE);
269  }
270  else
271  {
272  oldwidth = con.linewidth;
273  con.linewidth = width;
274  oldtotallines = con.totallines;
276  numlines = oldtotallines;
277 
278  if (con.totallines < numlines)
279  numlines = con.totallines;
280 
281  numchars = oldwidth;
282 
283  if (con.linewidth < numchars)
284  numchars = con.linewidth;
285 
286  memcpy (tbuf, con.text, CON_TEXTSIZE);
287  memset (con.text, ' ', CON_TEXTSIZE);
288 
289  for (i=0 ; i<numlines ; i++)
290  {
291  for (j=0 ; j<numchars ; j++)
292  {
293  con.text[(con.totallines - 1 - i) * con.linewidth + j] =
294  tbuf[((con.current - i + oldtotallines) %
295  oldtotallines) * oldwidth + j];
296  }
297  }
298 
299  Con_ClearNotify ();
300  }
301 
302  con.current = con.totallines - 1;
304 }
305 
306 
307 /*
308 ================
309 Con_Init
310 ================
311 */
312 void Con_Init (void)
313 {
314  con.linewidth = -1;
315 
316  Con_CheckResize ();
317 
318  Com_Printf ("Console initialized.\n");
319 
320 //
321 // register our commands
322 //
323  con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
324 
325  Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
326  Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
327  Cmd_AddCommand ("messagemode", Con_MessageMode_f);
328  Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
329  Cmd_AddCommand ("clear", Con_Clear_f);
330  Cmd_AddCommand ("condump", Con_Dump_f);
331  con.initialized = true;
332 }
333 
334 
335 /*
336 ===============
337 Con_Linefeed
338 ===============
339 */
340 void Con_Linefeed (void)
341 {
342  con.x = 0;
343  if (con.display == con.current)
344  con.display++;
345  con.current++;
347  , ' ', con.linewidth);
348 }
349 
350 /*
351 ================
352 Con_Print
353 
354 Handles cursor positioning, line wrapping, etc
355 All console printing must go through this in order to be logged to disk
356 If no console is visible, the text will appear at the top of the game window
357 ================
358 */
359 void Con_Print (char *txt)
360 {
361  int y;
362  int c, l;
363  static int cr;
364  int mask;
365 
366  if (!con.initialized)
367  return;
368 
369  if (txt[0] == 1 || txt[0] == 2)
370  {
371  mask = 128; // go to colored text
372  txt++;
373  }
374  else
375  mask = 0;
376 
377 
378  while ( (c = *txt) )
379  {
380  // count word length
381  for (l=0 ; l< con.linewidth ; l++)
382  if ( txt[l] <= ' ')
383  break;
384 
385  // word wrap
386  if (l != con.linewidth && (con.x + l > con.linewidth) )
387  con.x = 0;
388 
389  txt++;
390 
391  if (cr)
392  {
393  con.current--;
394  cr = false;
395  }
396 
397 
398  if (!con.x)
399  {
400  Con_Linefeed ();
401  // mark time for transparent overlay
402  if (con.current >= 0)
404  }
405 
406  switch (c)
407  {
408  case '\n':
409  con.x = 0;
410  break;
411 
412  case '\r':
413  con.x = 0;
414  cr = 1;
415  break;
416 
417  default: // display character and advance
418  y = con.current % con.totallines;
419  con.text[y*con.linewidth+con.x] = c | mask | con.ormask;
420  con.x++;
421  if (con.x >= con.linewidth)
422  con.x = 0;
423  break;
424  }
425 
426  }
427 }
428 
429 
430 /*
431 ==============
432 Con_CenteredPrint
433 ==============
434 */
435 void Con_CenteredPrint (char *text)
436 {
437  int l;
438  char buffer[1024];
439 
440  l = strlen(text);
441  l = (con.linewidth-l)/2;
442  if (l < 0)
443  l = 0;
444  memset (buffer, ' ', l);
445  strcpy (buffer+l, text);
446  strcat (buffer, "\n");
447  Con_Print (buffer);
448 }
449 
450 /*
451 ==============================================================================
452 
453 DRAWING
454 
455 ==============================================================================
456 */
457 
458 
459 /*
460 ================
461 Con_DrawInput
462 
463 The input line scrolls horizontally if typing goes beyond the right edge
464 ================
465 */
466 void Con_DrawInput (void)
467 {
468  int y;
469  int i;
470  char *text;
471 
472  if (cls.key_dest == key_menu)
473  return;
474  if (cls.key_dest != key_console && cls.state == ca_active)
475  return; // don't draw anything (always draw if not active)
476 
477  text = key_lines[edit_line];
478 
479 // add the cursor frame
480  text[key_linepos] = 10+((int)(cls.realtime>>8)&1);
481 
482 // fill out remainder with spaces
483  for (i=key_linepos+1 ; i< con.linewidth ; i++)
484  text[i] = ' ';
485 
486 // prestep if horizontally scrolling
487  if (key_linepos >= con.linewidth)
488  text += 1 + key_linepos - con.linewidth;
489 
490 // draw it
491  y = con.vislines-16;
492 
493  for (i=0 ; i<con.linewidth ; i++)
494 #ifdef QMAX
495  re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i], 1);
496 #else
497  re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]);
498 #endif
499 
500 // remove cursor
502 }
503 
504 
505 /*
506 ================
507 Con_DrawNotify
508 
509 Draws the last few lines of output transparently over the game top
510 ================
511 */
512 void Con_DrawNotify (void)
513 {
514  int x, v;
515  char *text;
516  int i;
517  int time;
518  char *s;
519  int skip;
520 
521  v = 0;
522  for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
523  {
524  if (i < 0)
525  continue;
526  time = con.times[i % NUM_CON_TIMES];
527  if (time == 0)
528  continue;
529  time = cls.realtime - time;
530  if (time > con_notifytime->value*1000)
531  continue;
532  text = con.text + (i % con.totallines)*con.linewidth;
533 
534  for (x = 0 ; x < con.linewidth ; x++)
535 #ifdef QMAX
536  re.DrawChar ( (x+1)<<3, v, text[x], 1);
537 #else
538  re.DrawChar ( (x+1)<<3, v, text[x]);
539 #endif
540 
541  v += 8;
542  }
543 
544 
545  if (cls.key_dest == key_message)
546  {
547  if (chat_team)
548  {
549  DrawString (8, v, "say_team:");
550  skip = 11;
551  }
552  else
553  {
554  DrawString (8, v, "say:");
555  skip = 5;
556  }
557 
558  s = chat_buffer;
559  if (chat_bufferlen > (viddef.width>>3)-(skip+1))
560  s += chat_bufferlen - ((viddef.width>>3)-(skip+1));
561  x = 0;
562  while(s[x])
563  {
564 #ifdef QMAX
565  re.DrawChar ( (x+skip)<<3, v, s[x], 1);
566 #else
567  re.DrawChar ( (x+skip)<<3, v, s[x]);
568 #endif
569  x++;
570  }
571 #ifdef QMAX
572  re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1), 1);
573 #else
574  re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1));
575 #endif
576  v += 8;
577  }
578 
579  if (v)
580  {
581  SCR_AddDirtyPoint (0,0);
583  }
584 }
585 
586 /*
587 ================
588 Con_DrawConsole
589 
590 Draws the console with the solid background
591 ================
592 */
593 void Con_DrawConsole (float frac)
594 {
595  int i, j, x, y, n;
596  int rows;
597  char *text;
598  int row;
599  int lines;
600  char version[64];
601  char dlbar[1024];
602 
603  lines = viddef.height * frac;
604  if (lines <= 0)
605  return;
606 
607  if (lines > viddef.height)
608  lines = viddef.height;
609 
610 // draw the background
611 #ifdef QMAX
612  re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback", 1);
613 #else
614  re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback");
615 #endif
616  SCR_AddDirtyPoint (0,0);
617  SCR_AddDirtyPoint (viddef.width-1,lines-1);
618 
619  Com_sprintf (version, sizeof(version), "v%4.2f", VERSION);
620  for (x=0 ; x<5 ; x++)
621 #ifdef QMAX
622  re.DrawChar (viddef.width-44+x*8, lines-12, 128+version[x],1);
623 #else
624  re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version[x] );
625 #endif
626 
627 // draw the text
628  con.vislines = lines;
629 
630 #if 0
631  rows = (lines-8)>>3; // rows of text to draw
632 
633  y = lines - 24;
634 #else
635  rows = (lines-22)>>3; // rows of text to draw
636 
637  y = lines - 30;
638 #endif
639 
640 // draw from the bottom up
641  if (con.display != con.current)
642  {
643  // draw arrows to show the buffer is backscrolled
644  for (x=0 ; x<con.linewidth ; x+=4)
645 #ifdef QMAX
646  re.DrawChar ( (x+1)<<3, y, '^', 1);
647 #else
648  re.DrawChar ( (x+1)<<3, y, '^');
649 #endif
650  y -= 8;
651  rows--;
652  }
653 
654  row = con.display;
655  for (i=0 ; i<rows ; i++, y-=8, row--)
656  {
657  if (row < 0)
658  break;
659  if (con.current - row >= con.totallines)
660  break; // past scrollback wrap point
661 
662  text = con.text + (row % con.totallines)*con.linewidth;
663 
664  for (x=0 ; x<con.linewidth ; x++)
665 #ifdef QMAX
666  re.DrawChar ( (x+1)<<3, y, text[x], 1);
667 #else
668  re.DrawChar ( (x+1)<<3, y, text[x]);
669 #endif
670  }
671 
672 //ZOID
673  // draw the download bar
674  // figure out width
675  if (cls.download) {
676  if ((text = strrchr(cls.downloadname, '/')) != NULL)
677  text++;
678  else
679  text = cls.downloadname;
680 
681  x = con.linewidth - ((con.linewidth * 7) / 40);
682  y = x - strlen(text) - 8;
683  i = con.linewidth/3;
684  if (strlen(text) > i) {
685  y = x - i - 11;
686  strncpy(dlbar, text, i);
687  dlbar[i] = 0;
688  strcat(dlbar, "...");
689  } else
690  strcpy(dlbar, text);
691  strcat(dlbar, ": ");
692  i = strlen(dlbar);
693  dlbar[i++] = '\x80';
694  // where's the dot go?
695  if (cls.downloadpercent == 0)
696  n = 0;
697  else
698  n = y * cls.downloadpercent / 100;
699 
700  for (j = 0; j < y; j++)
701  if (j == n)
702  dlbar[i++] = '\x83';
703  else
704  dlbar[i++] = '\x81';
705  dlbar[i++] = '\x82';
706  dlbar[i] = 0;
707 
708  sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
709 
710  // draw it
711  y = con.vislines-12;
712  for (i = 0; i < strlen(dlbar); i++)
713 #ifdef QMAX
714  re.DrawChar ( (i+1)<<3, y, dlbar[i], 1);
715 #else
716  re.DrawChar ( (i+1)<<3, y, dlbar[i]);
717 #endif
718  }
719 //ZOID
720 
721 // draw the input prompt, user text, and cursor if desired
722  Con_DrawInput ();
723 }
724 
725 
SCR_EndLoadingPlaque
void SCR_EndLoadingPlaque(void)
Definition: cl_scrn.c:598
Con_DrawInput
void Con_DrawInput(void)
Definition: console.c:466
int
CONST PIXELFORMATDESCRIPTOR int
Definition: qgl_win.c:35
client_state_t::attractloop
qboolean attractloop
Definition: client.h:173
DrawAltString
void DrawAltString(int x, int y, char *s)
Definition: console.c:49
chat_buffer
char chat_buffer[MAXCMDLINE]
Definition: keys.c:395
Con_ToggleChat_f
void Con_ToggleChat_f(void)
Definition: console.c:115
ca_disconnected
@ ca_disconnected
Definition: client.h:206
MAXCMDLINE
#define MAXCMDLINE
Definition: console.c:29
v
GLdouble v
Definition: qgl_win.c:143
x
GLint GLenum GLint x
Definition: qgl_win.c:116
viddef_t::width
int width
Definition: vid.h:29
i
int i
Definition: q_shared.c:305
ca_active
@ ca_active
Definition: client.h:209
Con_ToggleConsole_f
void Con_ToggleConsole_f(void)
Definition: console.c:75
buffer
GLenum GLfloat * buffer
Definition: qgl_win.c:151
viddef_t::height
int height
Definition: vid.h:29
VERSION
#define VERSION
Definition: qcommon.h:26
width
GLint GLsizei width
Definition: qgl_win.c:115
Con_Clear_f
void Con_Clear_f(void)
Definition: console.c:138
Con_DrawConsole
void Con_DrawConsole(float frac)
Definition: console.c:593
edit_line
int edit_line
Definition: keys.c:35
Cvar_Get
cvar_t * Cvar_Get(char *var_name, char *var_value, int flags)
Definition: cvar.c:127
FS_CreatePath
void FS_CreatePath(char *path)
Definition: files.c:125
chat_bufferlen
int chat_bufferlen
Definition: keys.c:396
cvar_s
Definition: q_shared.h:317
key_menu
@ key_menu
Definition: client.h:220
console_t::vislines
int vislines
Definition: console.h:44
viddef
viddef_t viddef
Definition: vid_dll.c:49
j
GLint j
Definition: qgl_win.c:150
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:517
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:507
Con_MessageMode_f
void Con_MessageMode_f(void)
Definition: console.c:229
console_t::linewidth
int linewidth
Definition: console.h:39
console_t::current
int current
Definition: console.h:33
Cmd_AddCommand
void Cmd_AddCommand(char *cmd_name, xcommand_t function)
Definition: cmd.c:691
key_game
@ key_game
Definition: client.h:220
CON_TEXTSIZE
#define CON_TEXTSIZE
Definition: console.h:27
Con_CheckResize
void Con_CheckResize(void)
Definition: console.c:253
cvar_s::value
float value
Definition: q_shared.h:324
Cbuf_AddText
void Cbuf_AddText(char *text)
Definition: cmd.c:90
console_t::text
char text[CON_TEXTSIZE]
Definition: console.h:32
MAX_OSPATH
#define MAX_OSPATH
Definition: q_shared.h:74
con
console_t con
Definition: console.c:24
key_console
@ key_console
Definition: client.h:220
NULL
#define NULL
Definition: q_shared.h:60
Con_ClearNotify
void Con_ClearNotify(void)
Definition: console.c:215
chat_team
qboolean chat_team
Definition: keys.c:394
DrawString
void DrawString(int x, int y, char *s)
Definition: console.c:35
name
cvar_t * name
Definition: cl_main.c:94
console_t::totallines
int totallines
Definition: console.h:40
SCR_AddDirtyPoint
void SCR_AddDirtyPoint(int x, int y)
Definition: cl_scrn.c:675
Con_Init
void Con_Init(void)
Definition: console.c:312
y
GLint y
Definition: qgl_win.c:115
console_t
Definition: console.h:28
re
refexport_t re
Definition: vid_dll.c:31
console_t::x
int x
Definition: console.h:34
Key_ClearTyping
void Key_ClearTyping(void)
Definition: console.c:64
client_static_t::download
FILE * download
Definition: client.h:249
Con_Linefeed
void Con_Linefeed(void)
Definition: console.c:340
NUM_CON_TIMES
#define NUM_CON_TIMES
Definition: console.h:25
Con_DrawNotify
void Con_DrawNotify(void)
Definition: console.c:512
console_t::times
float times[NUM_CON_TIMES]
Definition: console.h:46
client_static_t::state
connstate_t state
Definition: client.h:224
key_linepos
int key_linepos
Definition: keys.c:31
FS_Gamedir
char * FS_Gamedir(void)
Definition: files.c:590
Con_CenteredPrint
void Con_CenteredPrint(char *text)
Definition: console.c:435
Com_ServerState
int Com_ServerState(void)
Definition: common.c:275
Con_Dump_f
void Con_Dump_f(void)
Definition: console.c:151
console_t::display
int display
Definition: console.h:35
key_lines
char key_lines[32][MAXCMDLINE]
Definition: keys.c:30
refexport_t::DrawStretchPic
void(* DrawStretchPic)(int x, int y, int w, int h, char *name)
Definition: ref.h:192
Cvar_Set
cvar_t * Cvar_Set(char *var_name, char *value)
Definition: cvar.c:278
console_t::ormask
int ormask
Definition: console.h:37
Con_Print
void Con_Print(char *txt)
Definition: console.c:359
client_static_t::key_dest
keydest_t key_dest
Definition: client.h:225
client_static_t::downloadname
char downloadname[MAX_OSPATH]
Definition: client.h:251
refexport_t::DrawChar
void(* DrawChar)(int x, int y, int c)
Definition: ref.h:193
Com_Printf
void Com_Printf(char *fmt,...)
Definition: common.c:102
con_notifytime
cvar_t * con_notifytime
Definition: console.c:26
cls
client_static_t cls
Definition: cl_main.c:105
mask
GLint GLuint mask
Definition: qgl_win.c:317
key_message
@ key_message
Definition: client.h:220
cl
client_state_t cl
Definition: cl_main.c:106
M_ForceMenuOff
void M_ForceMenuOff(void)
Definition: menu.c:117
Com_sprintf
void Com_sprintf(char *dest, int size, char *fmt,...)
Definition: q_shared.c:1236
client.h
console_t::initialized
qboolean initialized
Definition: console.h:30
client_static_t::realtime
int realtime
Definition: client.h:228
client_static_t::downloadpercent
int downloadpercent
Definition: client.h:254
Cvar_VariableValue
float Cvar_VariableValue(char *var_name)
Definition: cvar.c:63
Con_MessageMode2_f
void Con_MessageMode2_f(void)
Definition: console.c:240